Create a smart, cross browser floating menu using Javascript

I recently wrote a Javascript library to show a floating menu that works cross browser and displays the menu based on a set of co-ordinates that you pass it.

The floating menu is just an absolute div that is hidden from view.

The library is smart in the sense that it determines where best to display the menu. It works out where you have scrolled to on the page and figures out if there is space at the top or bottom and to the left or right of where ever your target co-ordinates are.

This is useful where you want to display a menu, or some div, but you don’t know (or care) where the menu is going to be needed on the page.

<html>
<head>
<script type="text/javascript" charset="utf-8">
function $(id) {return document.getElementById(id);}
var smartpopup = (function () {    
    this._class = function () {
	    
	    this.showPopup = function (tp) {
			var popup = $('smartpopup');
	        var vp = this.getViewPos(); // view pos
	        var vs = this.getViewSize(); // view size
	        var ps = this.getElementSize(popup);
	        var a = x = y = 0;
	        var b = 1;
	        
	        var x_offset = y_offset = 0; //optional offset to where you click
	        
	        var target_point = x = ( tp[a] - x_offset );
	        var view_width = vs[a];
	        var view_position_width = vp[a];
	        var popup_width = ps[a];
	        var try_x = 0;
	        
		    //check there is room to the right of the click point
	        if ( ((target_point + popup_width) - view_position_width) > view_width) {
	        	//need to try and place it left of the click point
	        	try_x = (target_point + x_offset) - popup_width;
	        	if ( try_x < (view_width + view_position_width) && try_x > 0 ) {
	        		x = try_x;
	        	} 
	        }   
	        
	        target_point = y = ( tp[b] + y_offset );
	        var view_height = vs[b];
	        var view_position_height = vp[b];
	        var popup_height = ps[b];
	        var try_y = 0;
	        
	        //check there is room below click point for popup
	        if ( ( (target_point + popup_height) - view_position_height ) > view_height) {
	        	//see if there is room above
	        	try_y = ((target_point - y_offset) - popup_height);
	        	if ( try_y < (view_height + view_position_height) && try_y > 0 ) {
	        		y = try_y;
	        	} 
	        }
	
	        popup.style.left = x + 'px';
	        popup.style.top = y + 'px';
	        popup.style.display = 'block';
	    }
	
	    this.getElementPos = function (e) {
	        var e1 = e2 = e;
	        var x = y = 0;
	        if (e1.offsetParent) {
	            do {
	                x += e1.offsetLeft;
	                y += e1.offsetTop;
	            } while (e1 = e1.offsetParent);
	        }
	        while ((e2 = e2.parentNode) && e2.nodeName.toUpperCase() !== 'BODY') {
	            x -= e2.scrollLeft;
	            y -= e2.scrollTop;
	        }
	        return [x, y];
	    }
	
	
	    this.getElementSize = function (e) {
	        return [e.offsetWidth, e.offsetHeight];
	    }
	
	
	    this.getViewPos = function () {
	        if (typeof window.pageYOffset === 'number') {
	            return [window.pageXOffset, window.pageYOffset];
	        } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
	            return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
	        } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
	            return [document.body.scrollLeft, document.body.scrollTop];
	        } else {
	            return [0, 0];
	        }
	    }
	
	    this.getViewSize = function () {
	        if (typeof window.innerWidth === 'number') {
	            return [window.innerWidth, window.innerHeight];
	        } else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
	            return [document.documentElement.clientWidth, document.documentElement.clientHeight];
	        } else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
	            return [document.body.clientWidth, document.body.clientHeight];
	        } else {
	            return [0, 0];
	        }
	    }
	}

    return this._class;
})();

var sp = new smartpopup();

var isIE = document.all?true:false;

if (!isIE) document.captureEvents(Event.CLICK);

function getMousePosition (e) {
	var x,y;
	
	if (!isIE) {
		x = e.pageX;
		y = e.pageY;
	}
	if (isIE) {
		x = event.clientX + document.body.scrollLeft;
		y = event.clientY + document.body.scrollTop;
	}
	
	return sp.showPopup([x, y]);
}

document.onclick = getMousePosition;
</script>
</head>
<body>
<div id="smartpopup" style="position:absolute; display:none; width:175px; height:auto; top:0px; left:0px; z-index:10000; border:solid 1px #CCC; background-color:white; padding:0px 15px;font-family:Arial,Sans;box-shadow: -10px 10px 20px rgba(0, 0, 0, .5);-webkit-box-shadow: 0px 0px 6px rgba(0, 0, 0, .25);-moz-box-shadow: 0px 0px 6px rgba(0, 0, 0, .25);">HELLO WORLD!</div><span style="float:left;">&nbsp;</span><div id="popup_info" style="display:block;float:left;background:url(http://i0.poll.fm/images/ratings/info.png) no-repeat 3px 2px;width:16px;height:16px;cursor:pointer;" onmouseover="javascript:sp.showPopup(sp.getElementPos($('popup_info')));return false;"><span style="display:none;">i</span></div><span style="clear:both;">&nbsp;</span>
</body>
</html>

Hope this is of use to someone.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s