根据屏幕位置引导下拉菜单自动下拉?
Posted
技术标签:
【中文标题】根据屏幕位置引导下拉菜单自动下拉?【英文标题】:bootstrap drop-down menu auto dropup according to screen position? 【发布时间】:2014-02-09 13:05:01 【问题描述】:我想知道你们中是否有人准备好我要求的东西,以使我免于麻烦。我正在寻找的是一个下拉菜单,可以根据其在屏幕上的位置自动添加下拉类 - 并且当用户滚动或调整窗口大小时也会自动更改。
我正在寻找的已经在 bootstrap-select 插件中实现了,但是插件太“重”而无法使用。
【问题讨论】:
您是否要求我们推荐或查找工具、库? 我在问,但过去一个小时我一直在做这个,所以我想我可能在附近 有没有办法可以关闭这种行为?现在默认了 【参考方案1】:有点晚了,但因为与其他人完全不同,这是我的引导程序 3 解决方案。它全局适用于页面上的所有下拉菜单,也适用于动态创建或加载的下拉菜单。
请注意,我必须使用 shown.bs.dropdown
事件,因为尺寸仅在此时准备好。
$(document).on("shown.bs.dropdown", ".dropdown", function ()
// calculate the required sizes, spaces
var $ul = $(this).children(".dropdown-menu");
var $button = $(this).children(".dropdown-toggle");
var ulOffset = $ul.offset();
// how much space would be left on the top if the dropdown opened that direction
var spaceUp = (ulOffset.top - $button.height() - $ul.height()) - $(window).scrollTop();
// how much space is left at the bottom
var spaceDown = $(window).scrollTop() + $(window).height() - (ulOffset.top + $ul.height());
// switch to dropup only if there is no space at the bottom AND there is space at the top, or there isn't either but it would be still better fit
if (spaceDown < 0 && (spaceUp >= 0 || spaceUp > spaceDown))
$(this).addClass("dropup");
).on("hidden.bs.dropdown", ".dropdown", function()
// always reset after close
$(this).removeClass("dropup");
);
如果需要,可以使用@PeterWone's app height 魔法轻松改进它,目前这对我来说已经足够好了。
更新
这是一个代表这个解决方案的小提琴:http://jsfiddle.net/3s2efe9u/
【讨论】:
你能举一个小提琴的例子吗?它似乎不适用于我的情况。 如果按钮是 input-group-btn 的一部分,那么您需要将“.dropdown”替换为“.input-group-btn” 效果很好!!谢谢。 非常好,立即生效!谢谢@ZoltánTamási 迄今为止最好的解决方案。其他几个解决方案绑定了页面滚动事件等。这对性能来说很糟糕..【参考方案2】:我在大页面上提供的答案存在性能问题。
我通过使用getBoundingClientRect()
对它进行了“改进”,并向包含dropdows 的特定.btn-group
添加了一个新类。例如。 btn-group-dropup
.
您的里程可能会有所不同。
(function()
// require menu height + margin, otherwise convert to drop-up
var dropUpMarginBottom = 100;
function dropUp()
var windowHeight = $(window).height();
$(".btn-group-dropup").each(function()
var dropDownMenuHeight,
rect = this.getBoundingClientRect();
// only toggle menu's that are visible on the current page
if (rect.top > windowHeight)
return;
// if you know height of menu - set on parent, eg. `data-menu="100"`
dropDownMenuHeight = $(this).data('menu');
if (dropDownMenuHeight == null)
dropDownMenuHeight = $(this).children('.dropdown-menu').height();
$(this).toggleClass("dropup", ((windowHeight - rect.bottom) < (dropDownMenuHeight + dropUpMarginBottom)) && (rect.top > dropDownMenuHeight));
);
;
// bind to load & scroll - but debounce scroll with `underscorejs`
$(window).bind(
"resize scroll touchstart touchmove mousewheel": _.debounce(dropUp, 100),
"load": dropUp
);
).call(this);
【讨论】:
【参考方案3】:2019 年 1 月 7 日编辑:整理变量名称并稍微优化代码。
dropup = function()
$(".dropdown-toggle").each(function()
offsetTop=$(this).offset().top+$(this).height()-$(window).scrollTop();
offsetBottom=$(window).height()-$(this).height()-$(this).offset().top+$(window).scrollTop();
ulHeight=$(this).parents('.btn-group').find('ul').height();
if ((offsetBottom < ulHeight) && (offsetTop > ulHeight))
parent.addClass('dropup');
else
parent.removeClass('dropup');
);
$(window).on('load resize scroll', dropup);
【讨论】:
我认为你应该对你的第一个问题进行完整的编辑,并将这个(你的评论)答案整合到你编辑的问题中。也许是因为你只是要求找到一个没有代码或其他东西的工具...... 必须按照我的要求进行一些修改,就像一个魅力! :)【参考方案4】:我知道这是一个老问题,但它在 google 搜索结果中占了上风,所以这里有另一个对我有用的简单解决方案。调整if
语句中的150
,以适应您的下拉菜单需要多少空间。
var dropUp = function()
var windowHeight = $(window).innerHeight();
var pageScroll = $('body').scrollTop();
$( ".your-dropdown" ).each( function()
var offset = $( this ).offset().top;
var space = windowHeight - ( offset - pageScroll );
if( space < 150 )
$( this ).addClass( "dropup" );
else
$( this ).removeClass( "dropup" );
);
$(window).load(dropUp);
$(window).bind('resize scroll mousewheel', dropUp);
【讨论】:
【参考方案5】:与其在每次更改剪辑范围时都对每个菜单进行欺骗,为什么不采用 JIT 方法?
<body>
<div id="viewport-proxy" style="visibility:hidden;
position:absolute; height:100%; width:100%; margin:0; padding:0;
top:0; left: 0; right:0; bottom:0;"></div>
...
var w = window, d = document, e = d.documentElement;
var g = d.getElementsByTagName('body')[0];
function appHeight()
var a = w.innerHeight || e.clientHeight || g.clientHeight;
var b = $("#viewport-proxy").height();
return a < b ? a : b;
;
$(".dropdown").on('show.bs.dropdown', function ()
var windowHeight = appHeight();
var rect = this.getBoundingClientRect();
if (rect.top > windowHeight) return;
var menuHeight = $(this).children('.dropdown-menu').height();
$(this).toggleClass("dropup",
((windowHeight - rect.bottom) < menuHeight) &&
(rect.top > menuHeight));
);
显然,您必须在菜单来来去去时管理事件绑定。在 Nathan 的答案演变为上述内容的 Durandal 应用程序中,每一行都有一个上下文菜单,表示一个数据。我在数据加载时创建绑定,将它们保存在视图模型中并在停用时销毁它们。我不需要在激活时重新创建它们,因为这会调用数据加载例程,从而创建绑定。
我还添加了这个:
$(window).on("scroll resize", function () $(".dropdown.open").dropdown("toggle"); );
您将它放在 shell.js 中,以便它提前加载并影响所有视图。如果还不是很明显,它的作用是在窗口滚动或调整大小时关闭任何打开的下拉菜单。这符合 Windows 约定,并且在发生调整大小或滚动事件时无需重新计算定位。
注意 appHeight 函数。甚至 jQuery 也无法准确报告手机上的窗口高度,但 appHeight 在我迄今为止测试过的每个平台和浏览器(IE、Ch、FF 桌面、IE、Ch、Sa 移动)上都能正常工作。
在其当前版本中,它测量一个绝对定位的隐藏 div 集以填充视口。这适用于 android Chrome 但不适用于 IE,而代码中显示的另一种方法适用于 IE 但不适用于 Android Chrome,因此我使用两者并使用较小的数字。
在我的 Durandal 应用程序中,该函数被实现为应用程序对象的高度方法,但为了简洁明了,我将示例代码展平。
【讨论】:
+1 我正在考虑类似的事情,IMO 这是一种比静态处理菜单更好、更优雅的方法。【参考方案6】:我发现 Zoltán Tamási 的答案是一个很好的起点,但当下拉菜单应出现在带有垂直滚动条的 div 中时,这还不够。我需要该 div 的高度(下面代码中的 scrollHeight 变量)来确定下拉菜单是否会在底部占用太多空间。
jquery(document).on("show.bs.dropdown", ".dropdown", function ()
var $ul = jquery(this).children(".dropdown-menu");
var menuHeight = $ul.height()
var buttonHeight = jquery(this).children(".dropdown-toggle").height();
var scrollHeight = $ul.parent().offsetParent().height();
var menuAnchorTop = $ul.parent().position().top + buttonHeight;
// how much space would be left at the top
var spaceUp = (menuAnchorTop - buttonHeight - menuHeight);
// how much space would be left at the bottom
var spaceDown = scrollHeight - (menuAnchorTop + menuHeight);
// Switch to dropup if more space there
if (spaceDown < 0 && (spaceUp >= 0 || spaceUp > spaceDown))
jquery(this).addClass("dropup");
).on("hidden.bs.dropdown", ".dropdown", function()
// always reset after close
jquery(this).removeClass("dropup");
);
【讨论】:
【参考方案7】:我已经检查了性能和浏览器支持(chrome、Firefox、Edge、IE 10 及更高版本)。
我已经介绍了垂直和水平自动定位。
代码:
(function ()
var dd_obj =
jq_win :jQuery(window),
jq_doc :jQuery(document),
function selfCalc(ele)
var $this = jQuery(ele),
$dd_menu = $this.children(".dropdown-menu"),
ddOffset = $this.offset(),
ddMenu_posTop = dd_obj.jq_win.outerHeight() - (($this.outerHeight() + ddOffset.top + $dd_menu.outerHeight()) - dd_obj.jq_doc.scrollTop());
ddMenu_posRight = dd_obj.jq_win.outerWidth() - (($this.outerWidth() + ddOffset.left + $dd_menu.outerWidth()) - dd_obj.jq_doc.scrollLeft());
(ddMenu_posTop <= 0) ? $this.addClass("dropup") : $this.removeClass("dropup");
(ddMenu_posRight <= 0) ? $this.find('.dropdown-menu').addClass("dropdown-menu-right") : $this.find('.dropdown-menu').removeClass("dropdown-menu-right");
jQuery('body').on("shown.bs.dropdown", ".dropdown", function ()
var self = this;
selfCalc(self)
dd_obj.jq_win.on('resize.custDd scroll.custDd mousewheel.custDd',function()
selfCalc(self)
)
).on("hidden.bs.dropdown", ".dropdown", function()
// there is no need to keep the window event after dropdown has closed,
// still if we keep the event then there is no use
dd_obj.jq_win.off('resize.custDd scroll.custDd mousewheel.custDd')
);
)();
【讨论】:
以上是关于根据屏幕位置引导下拉菜单自动下拉?的主要内容,如果未能解决你的问题,请参考以下文章