I was wondering if any of you has what I am asking for ready, to save me from the trouble. What I am looking for is for a dropdown menu to have the dropup class automatically ad
Rather than diddling every menu every time something changes clipping bounds, why not take a JIT approach?
<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));
});
Obviously you'll have to manage event bindings as menus come and go. In the Durandal app for which Nathan's answer morphed into the above, there is a context menu for each row representing a datum. I create the bindings on data load, keep them in the view-model and destroy them on deactivate. I don't need to recreate them on activate because that calls the data load routine, which in turn creates the bindings.
I also added this:
$(window).on("scroll resize", function () { $(".dropdown.open").dropdown("toggle"); });
You put this in shell.js so it's loaded early and affects all views. If it's not already obvious, what this does is close any open dropup when the window scrolls or resizes. This conforms with Windows convention and makes it unnecessary to recompute positioning when resize or scroll events occur.
Note the appHeight function. Even jQuery fails to accurately report window height on mobile phones, but appHeight works correctly on every platform and browser that I've tested so far (IE,Ch,FF desktop, IE,Ch,Sa mobile).
In its current incarnation it measures an absolutely positioned hidden div set to fill the viewport. This works on Android Chrome but not IE, whereas the other method shown in the code works with IE but not Android Chrome, so I use both and use the lower number.
In my Durandal app the function is implemented as a height method on the app object but for brevity and clarity I flattened the example code.