1/* SWIM2.0 :: Simple website menu 2**************************************************************** 3* DOM scripting by brothercake -- http://www.brothercake.com/ 4* Licensed under GPL -- http://www.gnu.org/copyleft/gpl.html 5**************************************************************** 6* For professional menu solutions visit -- http://www.udm4.com/ 7**************************************************************** 8*/ 9 10 11 12//initialise the menu(s) 13//you can replace this with an encapsulated onload if necessary 14//http://www.brothercake.com/site/resources/onload/ 15//you can also take it out of this script and put it somewhere else 16window.onload = function() 17{ 18 //create a new menu ('menu-id', 'orientation') 19 var verticals = new simpleMenu('menu-v', 'vertical'); 20 var horizontals = new simpleMenu('menu-h', 'horizontal'); 21}; 22 23 24 25 26 27 28//menu object constructor 29function simpleMenu(navid, orient) 30{ 31 //if the dom is not supported, or this is opera 5 or 6, don't continue 32 if(typeof document.getElementById == 'undefined' || /opera[\/ ][56]/i.test(navigator.userAgent)) { return; } 33 34 //identify konqueror 35 this.iskde = navigator.vendor == 'KDE'; 36 37 //identify internet explorer [but both opera and konqueror recognise the .all collection] 38 this.isie = typeof document.all != 'undefined' && typeof window.opera == 'undefined' && !this.iskde; 39 40 //identify old safari [< 1.2] 41 this.isoldsaf = navigator.vendor == 'Apple Computer, Inc.' && typeof XMLHttpRequest == 'undefined'; 42 43 //ul tree 44 this.tree = document.getElementById(navid); 45 46 //if it exists 47 if(this.tree != null) 48 { 49 //get trigger elements 50 this.items = this.tree.getElementsByTagName('li'); 51 this.itemsLen = this.items.length; 52 53 //initialise each trigger, using do .. while because it's faster 54 var i = 0; 55 do 56 { 57 this.init(this.items[i], this.isie, this.isoldsaf, this.iskde, navid, orient); 58 } 59 while (++i < this.itemsLen); 60 } 61} 62 63 64//trigger initialiser 65simpleMenu.prototype.init = function(trigger, isie, isoldsaf, iskde, navid, ishoriz) 66{ 67 //store menu object, or null if there isn't one 68 //extend it as a property of the trigger argument 69 //so it's global within [and unique to] the scope of this instantiation 70 //which is the same trick as going "var self = this" 71 trigger.menu = trigger.getElementsByTagName('ul').length > 0 ? trigger.getElementsByTagName('ul')[0] : null; 72 73 //store link object 74 trigger.link = trigger.getElementsByTagName('a')[0]; 75 76 //store whther this is a submenu or child menu 77 //a submenu's parent node will have the navbar id 78 trigger.issub = trigger.parentNode.id == navid; 79 80 //whether this is a horizontal navbar 81 trigger.ishoriz = ishoriz == 'horizontal'; 82 83 //menu opening events 84 //onfocus doesn't bubble in ie, but its proprietary 'onactivate' event does 85 //which works in win/ie5.5+ 86 this.openers = { 'm' : 'onmouseover', 'k' : (isie ? 'onactivate' : 'onfocus') }; 87 88 //bind menu openers 89 for(var i in this.openers) 90 { 91 trigger[this.openers[i]] = function(e) 92 { 93 //set rollover persistence classname -- we have to check for an existing value first 94 //because some opera builds don't allow the class attribute to have a leading space 95 //don't do persistent rollovers for konqueror, because they stick in kde <= 3.0.4 96 if(!iskde) { trigger.link.className += (trigger.link.className == '' ? '' : ' ') + 'rollover'; } 97 98 //if trigger has a menu 99 if(trigger.menu != null) 100 { 101 //show the menu by positioning it back on the screen 102 //we can use the same positions as in pure CSS for most browsers ['css'] 103 //but we have to compute the positions for ie ['compute'] 104 //because it uses position:relative on <a> 105 //whereas the others have position:relative on <li> 106 //we also need to use those values for old safari builds ['compute'] 107 //because the regular positioning doesn't work 108 109 //if this is a horizontal navbar 110 //set the left position to auto [css] or compute a position [compute] 111 if(trigger.ishoriz) { trigger.menu.style.left = (isie || isoldsaf) ? trigger.offsetLeft + 'px' : 'auto'; } 112 113 //if this is a horizontal navbar and a first-level submenu 114 //set the top position to auto [css] or compute a position [compute] 115 //otherwise set it to 0 [css] or compute a position [compute] 116 trigger.menu.style.top = (trigger.ishoriz && trigger.issub) ? (isie || (trigger.ishoriz && isoldsaf)) ? trigger.link.offsetHeight + 'px' : 'auto' : (isie || (trigger.ishoriz && isoldsaf)) ? trigger.offsetTop + 'px' : '0'; 117 } 118 }; 119 } 120 121 122 //menu closing events 123 //'ondeactivate' is the equivalent blur handler for 'onactivate' 124 this.closers = { 'm' : 'onmouseout', 'k' : (isie ? 'ondeactivate' : 'onblur') }; 125 126 //bind menu closers 127 for(i in this.closers) 128 { 129 trigger[this.closers[i]] = function(e) 130 { 131 //store event-related-target property 132 this.related = (!e) ? window.event.toElement : e.relatedTarget; 133 134 //if event came from outside current trigger branch 135 if(!this.contains(this.related)) 136 { 137 //reset rollover persistence classname; not for konqueror 138 if(!iskde) { trigger.link.className = trigger.link.className.replace(/[ ]?rollover/g, ''); } 139 140 //if trigger has a menu 141 if(trigger.menu != null) 142 { 143 //hide menu using left for a horizontal menu, or top for a vertical 144 trigger.menu.style[(trigger.ishoriz ? 'left' : 'top')] = trigger.ishoriz ? '-10000px' : '-100em'; 145 } 146 } 147 }; 148 } 149 150 151 //contains method by jkd -- http://www.jasonkarldavis.com/ 152 //not necessary for ie because we're re-creating in ie-proprietary method 153 //and it would throw errors in mac/ie5 anyway 154 //not actually necessary for opera 7 either, because it's already implemented 155 //but it won't do any harm, so spoofing doesn't matter 156 if(!isie) 157 { 158 trigger.contains = function(node) 159 { 160 if (node == null) { return false; } 161 if (node == this) { return true; } 162 else { return this.contains(node.parentNode); } 163 }; 164 } 165} 166 167