LMLPHP后院

前端技术之导航栏浮动最佳实践技术

maybe yes 发表于 2015-05-29 00:28

关于网页的导航菜单,特别是后台,最古老的做法就是使用 FrameSet。到现在 FrameSet 的这种做法已经过时,并且会带来很多安全问题。于是很多网站在前台都使用浮动的方式来解决这个问题,当页面向上滚动时,到了一定的位置,DIV 导航栏会固定住,这样用户页面任何位置都能看到重要的导航区域。另外有一种做法用户体验也比较好,比如在腾讯网,当用户稍微向上滚动的时候会出现横向的导航栏,向下滚动导航栏不会出现,这样用户也能很方便的找到导航栏,只是很多人不知道,或者不习惯这样的方式。

我开始在做 LMLPHP 官方网站的文档的时候,也遇到了这样的问题,如何用更好的方式来展示左侧菜单导航区域。LMLPHP 官方文档的做法是计算导航左侧和右侧主体内容的高度,结合浏览器窗口可见区域 viewport 的高度,最后得出左侧 DIV 的最佳高度,采用悬停展示滚动条(如果菜单被展开,高度超出范围)的方式和页面向下滚动固定左侧导航的方式来提升用户体验。这样的做法还算能够接受,唯一的不好就是会鼠标悬停时会出现滚动条,并且高度计算很难做到合适,兼容性比较麻烦,为了兼容大部分浏览器也是花费了很大的精力,偶尔会发现导航消失的问题。

前段时间在逛 Facebook 的动态的时候,发现他们做的很好。FaceBook 的左侧内容区域的做法是随着页面运动的,左侧导航区域的上下两端都会根据相对右侧主体内容的位置来判断是否应该固定,当顶部内容不够,向上滚动时则固定,底部内容不够,向下滚动时则固定,固定的位置范围和右侧主体内容的高度范围一致。于是将本博客右侧也做了相同的改动,由于精力和能力都不够,暂时是根据改变 Margin-Top 来实现的,或许通过改变 position 为 fixed 效果会更好,除 FireFox 外的浏览器都会出现滚动时不够稳定,会有轻微的颤抖,目前测试支持 IE6 及以上浏览器、Firefox、Chrome。在测试的过程中,发现本博客在 IE6 中样式出现问题,有图片的列表页内容被图片撑坏了,发现 IE6 是不支持 max-height 和 max-width 属性的。看了同行的博客 IE6 访问直接 403 ,并显示 Fuck IE 6,看最近几个月的访问统计,IE6 的流量几乎没有,Window 7 占据了一大半的市场份额。

下面给出具体实现技术上的部分 JavaScript 代码代码来自 LMLJS 框架需要朋友可以在 LMLJS 上获取,目前代码没有合到主分支,在 develop 分支上。

计算元素左上角相对页面顶部的距离,代码如下:

<script>
function getElementOffsetTop(element){
    var actualTop = element.offsetTop
        ,offsetParentElement = element.offsetParent;
    if( offsetParentElement == null && element.parentNode ){
        /* when style display is none */
        var parentNode = element.parentNode;
        while( offsetParentElement == null ){
            offsetParentElement = parentNode.offsetParent;
            parentNode = parentNode.parentNode;
            if( !parentNode ){
                break;
            }
        }
    }
    while ( offsetParentElement !== null /* document.body */ ){
        actualTop += (offsetParentElement.offsetTop+offsetParentElement.clientTop);
        offsetParentElement = offsetParentElement.offsetParent;
    }
    return actualTop;
}
</script>

计算页面当前被滚动后页面隐藏部分的高度,代码如下:

<script>
function getPageScrollOffset() {
    var doc = document, w = window;
    var x, y, docEl;

    if ( typeof w.pageYOffset === 'number' ) {
        x = w.pageXOffset;
        y = w.pageYOffset;
    } else {
        docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat') ?
            doc.documentElement : doc.body;
        x = docEl.scrollLeft;
        y = docEl.scrollTop;
    }
    return {x : x, y : y};
}
</script>

计算当前窗口可见区域大小,代码如下:

<script>
function getWindowViewport() {
    var doc = document, w = window;
    var docEl = (doc.compatMode && doc.compatMode === 'CSS1Compat') ?
            doc.documentElement: doc.body;

    var width = docEl.clientWidth;
    var height = docEl.clientHeight;

    if ( w.innerWidth && width > w.innerWidth ) {
        width = w.innerWidth;
        height = w.innerHeight;
    }

    return {width : width, height : height};
}
</script>

除去上面的代码提供的功能外,剩下的就是逻辑上和一些细节上的东西。比如我们不能为了节省性能,没有在每次滚动触发的时候计算相对主体 DIV 区域的高度,因为页面的内容可能在滚动的时候会发生变化,所以必须每次都得重新计算窗口大小和相对滚动范围的高度;当然,如果站点的前端 JS 代码够牛逼,模块之间的变化能够时刻发出通知,也可以不必这样做了。

这次,在做这个功能的时候,发现通用的获取 CSS方法(如:window.getComputedStyle 和 element.currentStyle)并不能很好的获取元素的高度属性。比如在 IE 8,获取到的高度值是 auto,而通过元素的 offsetHeight 属性是可以的,这也或许就是为什么 JQuery 会专门有个 height() 和 width() 方法了。

相关文章
2024-04-23 19:54:52 1713873292 0.018427