背景

有两个页面使用同样的布局,CSS代码如下:

body {
  width: 500px;
  margin: 0 auto;
}

其中一个页面由于内容比较少页面不需要滚动,即不出现滚动条.另一个页面内容很多,出现垂直方向的滚动条.

问题

这时候在这两个页面之间切换会出现页面水平方向有微小移动,造成页面晃动的感觉, 视觉体验上感觉很不舒服.

原因

这是因为垂直方向的滚动条的出现占据了一定的宽度,导致两个页面body元素的margin属性的auto的最终计算值不同.

解决方法

方法一: 最简单粗暴的就是不管页面高度是多少全部都显示垂直方向的滚动条就好了:

body {
  overflow-y: scroll;
}

方法二: 计算滚动条宽度, 待DOM加载完成后重新设定margin的值:

  ###
   * This is a solution for preventing pagelayout
   * (eg: body {margin: 0 auto})
   * break when jumping between pages which have 
   * the same layout but the one(the page height is enough) 
   * have scrollbar and other have not scrollbar.
   * @param  String innerEle The inner element who affected
   * @param  String outerEle The outter element in which 
   *                          the inner element placed, if 
   *                          omit the default is window
  ###
  preventPageShakingFromScrollbar = (innerEle,outerEle)->
    # getScrollerWidth
    scr = document.createElement('div')
    scr.style.position = 'absolute'
    scr.style.top = '-1000px'
    scr.style.left = '-1000px'
    scr.style.width = '100px'
    scr.style.height = '50px'
    scr.style.overflow = 'hidden'
    inn = document.createElement('div')
    inn.style.width = '100%'
    inn.style.height = '200px'
    scr.appendChild(inn)
    document.body.appendChild(scr)
    wNoScroll = inn.offsetWidth
    scr.style.overflow = 'auto'
    wScroll = inn.offsetWidth
    document.body.removeChild(document.body.lastChild)
    scrollerW = wNoScroll - wScroll

    # is scrollbar exists
    root = if document.compatMode=='BackCompat' then document.body else document.documentElement
    isVerticalScrollbar= root.scrollHeight>root.clientHeight

    outerEle = if outerEle then outerEle else window
    # TODO: make code not depend on jQuery
    $outerEle = $(outerEle)
    $innerEle = $(innerEle)
    containerW = $outerEle.width()
    containeeW = $innerEle.outerWidth()
    originML = (containerW - containeeW)/2
    # console.log(originML)
    # console.log(scrollerW)
    # console.log(isVerticalScrollbar)
    
    if isVerticalScrollbar
      $innerEle.css('marginLeft',originML+scrollerW/2)
    else
      $innerEle.css('marginLeft',originML)

PS:

在写这个代码的时候发现要获得CSS属性auto解析出来的值需要用window.getComputedStyle这个方法. 而不能通过jQeury.css(‘marginLeft’)这样来获得. 在最新的(22.0)火狐浏览器中无法通过window.getComputedStyle来或者这个auto解析后的值.所以上面的代码人为的计算了解析后的值.