【问题标题】:Detect if dropdown navigation would go off screen and reposition it检测下拉导航是否会离开屏幕并重新定位
【发布时间】:2012-07-16 20:30:02
【问题描述】:

我有你典型的下拉导航,我正在努力确保下拉菜单链接始终可访问和可见:

<li><a href="#">Link 1</a>
    <ul>
        <li><a href="#">Link 1</a></li>
        <li><a href="#">Link 2</a></li>
        <li><a href="#">Link 3</a></li>
    </ul>
</li>
<li><a href="#">Link 2</a>
    <ul>
        <li><a href="#">Link 1</a></li>
        <li><a href="#">Link 2</a></li>
        <li><a href="#">Link 3</a></li>
    </ul>
</li>
<!-- etc. -->
</ul>

CSS 真的没有什么特别的(去除了颜色和背景):

.dropdown,
.dropdown li,
.dropdown ul {
    list-style:none;
    margin:0;
    padding:0;
}
.dropdown {
    position:relative;
    z-index:10000;
    float:left;
    width:100%;
}
.dropdown ul {
    position:absolute;
    top:100%;
    visibility:hidden;
    display:none;
    width:16em;
}
.dropdown ul ul {
    top:0;
    left:100%;
}
.dropdown li {
    position:relative;
    float:left;
}
.dropdown li:hover{
    z-index:910;
}
.dropdown ul:hover,
.dropdown li:hover > ul,
.dropdown a:hover + ul,
.dropdown a:focus + ul {
    visibility:visible;
    display:block;
}
.dropdown a {
    display:block;
    padding:1em 2em;
}
.dropdown ul li {
    width:100%;
}

有未知数量的顶级链接(它们是由用户创建的)。我遇到的问题是,如果顶级链接离右侧太远,有时下拉菜单(向右)会离开屏幕。我添加了一些 CSS 来弥补:

.dropdown > li:last-child ul { /* ...or use a class on the last link for IE */
    right:0;
}

现在最后一个移到左边而不是屏幕外,这很好,但有几个问题:

  1. 我并不总是需要这些样式用于最后一个链接,因为它并不总是位于屏幕边缘(例如只有 3 个链接)。
  2. 当调整浏览器窗口的大小时,链接会相互堆叠(根据设计)。有时,位于序列中间的链接会出现在右边缘,并且它们的下拉菜单会被截断。
  3. 有时“倒数第二个”链接的菜单也会超出边界。

调整此演示中的面板大小以了解我的意思(红色区域被视为“屏幕外”) http://jsfiddle.net/G7qfq/

多年来,我一直在努力解决这个令人讨厌的常见问题,但从未找到令人满意的解决方案。有什么方法可以检查下拉菜单是否会离开屏幕,如果是,请添加/删除 class 名称或其他内容,以便我可以使用 CSS 将其保留在屏幕上?

我可能使用的一个线索是,如果菜单确实离开屏幕,它总是会在窗口底部产生一个垂直滚动条,但我不确定如何使用这些知识.我尝试了this question关于检测垂直滚动条的公认答案,但由于某种原因,它总是返回true,并且总是添加“边缘”类(也许时间有问题?):

$(".dropdown li").on('mouseenter mouseleave', function (e) {

    // Get the computed style of the body element
    var cStyle = document.body.currentStyle||window.getComputedStyle(document.body, "");

    // Check the overflow and overflowY properties for "auto" and "visible" values
    hasVScroll = cStyle.overflow == "visible" 
             || cStyle.overflowY == "visible"
             || (hasVScroll && cStyle.overflow == "auto")
             || (hasVScroll && cStyle.overflowY == "auto");

    if (hasVScroll) {
        $(this).addClass('edge');
    } else {
        $(this).removeClass('edge');
    }
});​

使用 javascript 演示:http://jsfiddle.net/G7qfq/2/

说真的,我不想看到垂直滚动条,哪怕是一瞬间,所以我不确定这是要走的路,而且可能会出现误报(滚动条出于其他原因)。

我也尝试了this answer 中的解决方案,我承认,我不太明白,无法让它工作:http://jsfiddle.net/G7qfq/3/

$(".dropdown li").on('mouseenter mouseleave', function (e) {

    var elm = $('ul:first', this);
    var off = elm .offset();
    var t = off.top;
    var l = off.left;
    var h = elm.height();
    var w = elm.width();
    var docH = $(window).height();
    var docW = $(window).width();

    var isEntirelyVisible = (t > 0 && l > 0 && t + h < docH && l+ w < docW);

    if ( ! isEntirelyVisible ) {
        $(this).addClass('edge');
    } else {
        $(this).removeClass('edge');
    }
});​

我假设该解决方案需要 javascript,并且我正在使用 jQuery,但我不知道如何解决该问题。有什么想法吗?

【问题讨论】:

  • stackoverflow.com/questions/1725508/… 这可能会有所帮助。
  • 我会看看并更新我的结果,谢谢。再想一想,“滚动条检测”方法不是很好,如果有效,可能会导致误报(滚动条是另一个原因)。
  • 是的,非常类似,除了顶级下拉菜单和可堆叠的导航项目(如果视口很小)。我会看看我是否可以挑选出该代码并找到有用的东西,感谢您理解我的问题。
  • @WesleyMurch - 有点老了。前段时间写的,最后没用。我确信有足够的空间进行更新和改进。希望对您有所帮助。

标签: javascript jquery html css


【解决方案1】:

我想你就快到了……

您真的应该只对涉及宽度的计算感兴趣。如果下拉元素的宽度和该元素的偏移量大于容器的宽度,则需要切换菜单。

$(function () {
    $(".dropdown li").on('mouseenter mouseleave', function (e) {
        if ($('ul', this).length) {
            var elm = $('ul:first', this);
            var off = elm.offset();
            var l = off.left;
            var w = elm.width();
            var docH = $(".container").height();
            var docW = $(".container").width();

            var isEntirelyVisible = (l + w <= docW);

            if (!isEntirelyVisible) {
                $(this).addClass('edge');
            } else {
                $(this).removeClass('edge');
            }
        }
    });
});

http://jsfiddle.net/G7qfq/582/

【讨论】:

  • 你成功了!直到我实际发布问题(当然),我的大部分想法都没有出现,我的大脑因工作而被烧毁,我没有意识到我的错误。衷心感谢您,此代码肯定会在工具箱中。
  • 即使在您的 jsfiddle 中,这似乎也会给出错误“未捕获的类型错误:无法读取未定义的属性 'left'”。有谁知道如何解决这个问题?
  • @Aaron 是的,不错的收获...查看更新。这些错误是由处理程序在子嵌套项不存在时尝试重新定位它们引起的!
  • 嗨@r0m4n 感谢您的出色回答。但我有一个小问题,我想用于第二级,但它不起作用。
  • @r0m4n 这可以与多级下拉菜单一起使用吗?我有一个下拉 2 个级别,需要能够从第一个列表中偏移第二个列表。
【解决方案2】:

这是一个可用于向右或向下弹出菜单的函数(基于@r0m4n 的代码):

function fixFlyout (containerElement, parentElement,flyoutElement,flyoutDirection) {
    $(parentElement).on('mouseenter mouseleave', function (e) {
        var element = $(flyoutElement, this);
        var offset = element .offset();
        switch(flyoutDirection) {
            case 'down':
                var top = offset.top;
                var height = element.height();
                var windowHeight = $(containerElement).height();
                var isEntirelyVisible = (top + height <= windowHeight);
                break;
            case 'right':
                var left = offset.left;
                var width = element.width();
                var windowWidth = $(containerElement).width();
                var isEntirelyVisible = (left + width <= windowWidth);
                break;
        }
        if (!isEntirelyVisible ) {
            $(element).addClass('edge');
        } else {
            $(element).removeClass('edge');
        }
    });
}
//Level 1 Flyout
fixFlyout(containerElement = '.header',parentElement = '.header .navigation>.menu>.expanded',flyoutElement = '.menu:first',flyoutDirection = 'down');

【讨论】:

  • 你能添加一个显示这个正在使用的小提琴吗?我希望能够在嵌套列表中包含嵌套列表,用于向右或向左移动的多级下拉列表
  • 看起来像一个错字,以防 'right' var isEntirelyVisible 应该是左 + 宽度,是吗?
【解决方案3】:

我无法让 addClass('edge') 正常工作,但我能够修改相关元素的 CSS 以实现该行为。 (仅对 r0m4n 稍作修改):

        //Align submenu to right if cut-off from window
        $(function () {
            $(".dropdown").on('mouseenter mouseleave', function (e) {
                if ($('.dropdown-content', this).length) {
                    var dropdownElement = $('.dropdown-content:first', this);
                    var elementOffset = dropdownElement.offset();
                    var elementOffsetLeft = elementOffset.left;
                    var elementWidth = dropdownElement.width();
                    var pageHeigth = $(".show-on-scroll-wrapper").height();
                    var pageWidth = $(".show-on-scroll-wrapper").width();
                    //if left offset + width of dropdown is bigger than container width then it is cut-off
                    if ((elementOffsetLeft + elementWidth) > pageWidth) {
                        //Align Right
                        $(".dropdown-content").css({ "left":"auto", "right":"0", "margin-right":"-10px;"});
                    } else {
                        //Align Left
                        $(".dropdown-content").css({ "left":"0", "right":"auto", "margin-left": "-10px;" });
                    }
                }
            });
        });

【讨论】:

    猜你喜欢
    • 2021-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多