Here’s the js I promised. I’m afraid that it’s tailored to my use, e.g. I have a bit in there that modifies some css specific to how my menu is styled. It also won’t work if your ul or lis have padding or margin applied. So I’m not sure how useful it will be to anyone. But at least it should provide a starting point if you want a similar function.
It doesn’t work in IE6 or 7, you could add a conditional statement to set the display to inline instead of inline-block in IE if you wanted support for those browsers. Sometimes in IE the end item drops off the end of the menu onto the next line, but I haven’t had time to debug this yet.
Since the site I’m doing this for doesn’t really need jQuery, I decided to do it in raw js, which means I needed a couple of helper functions. So I stole Craig Buckler’s each function from his series of articles on Native JavaScript Equivalents of jQuery Methods.
I might try and write a more universal function with an example page sometime, but unfortunately I don’t have the time at the moment.
(function(){
/**loop through an array / list, passing each item in the list to the callback function
*The callback function receives two arguments, the current item in the list, and the index of that item in the list
*If the callback function returns false, then the loop will exit
*(Taken from http://www.sitepoint.com/jquery-vs-raw-javascript-3-events-ajax/)
*@param obj The array or nodeList to loop through
*@param fn The callback function that each item in the list will be passed
*/
function Each(obj, fn) {
if (obj.length) for (var i = 0, ol = obj.length, v = obj[0]; i < ol && fn(v, i) !== false; v = obj[++i]);
else for (var p in obj) if (fn(obj[p], p) === false) break;
};
/**
*Cross-browser method to attach an event listener
*@param HTMLObject el The element to attach the event listener to
*@param string eventType The type of event to listen for
*@param function fn The function to fire when the event occurs
*/
function addEvent(el, eventType, fn) {
if (el.addEventListener) {
return el.addEventListener(eventType, fn);
}
else if (el.attachEvent) {
return el.attachEvent('on'+eventType, fn);
}
}
/**
*Space items equally across a horizontal menu so that they fill the whole menu by applying
*the same amount of padding to the anchor element contained in each list item.
*@param HTMLObject The ul or ol html object that is the menu
*@param number The minimum amount of padding to apply
*/
function justifyMenu(menu,minPadding) {
//check we have an ol or ul menu
if (menu.tagName != 'UL' && menu.tagName != 'OL') {
throw new TypeError('menu should be an unordered or ordered list HTML Object');
}
//initialise some variables
var lis = menu.children,
as=[],
contentWidth=0,
menuWidth;
//set minPadding to a default value of 5 if not provided
minPadding = (typeof minPadding == 'number') ? minPadding : 5;
//set the list items to wrap to their contents and get their width
Each(lis, function(as){return function(el){
el.style.display='inline-block';
//for IE7 set display to inline and add zoom: 1
//get the child anchor tag
Each(el.children, function(as){return function(el){
if (el.tagName == 'A') {
//reset the anchor padding to the minimum
el.style.paddingLeft=minPadding+'px';
el.style.paddingRight=minPadding+'px';
//add the anchor to the array of anchors to have their padding modified
as.push(el);
}
}; }(as))
//now we can get the width of the list item
contentWidth+=el.offsetWidth;
};}(as));
//only justify the menu items if they are smaller with the minimum padding applied than the menu width
if (contentWidth < (menuWidth=menu.offsetWidth)) {
//Taking the contentWidth away from the menuWidth gives us the total spare space
//Dividing the spare space by the number of list items gives us the total padding to apply to each item
//Dividing this by 2 gives us the amount of padding to apply left and right
//We need to round the value down otherwise IE9 & 10 will suffer from rounding issues, pushing the final list item off the end and onto a new row
var padding=Math.floor((menuWidth-contentWidth)/(lis.length*2))+minPadding;
Each(as, function(el){
el.style.paddingLeft=padding + 'px';
el.style.paddingRight=padding + 'px';
});
//Fix the display of the submenus now we are using inline-block display for the parents instead of table-cell (see ".main-navigation li ul" ruleset in theme's style.css)
Each( menu.getElementsByTagName('ul'), function(el){
var s=el.style;
s.position='absolute';
s.top='100%';
s.zIndex = 1;
s.height = 'auto';
});
}
}
//don't justify the menu for IE7
if(document.getElementsByTagName('html')[0].className.indexOf('ie7') == -1){
justifyMenu(document.getElementById('myMenu'));
addEvent(window, "resize", function(e) {
justifyMenu(document.getElementById('myMenu'));
});
}
})();