2 Behaviour v1.1 by Ben Nolan, June 2005. Based largely on the work
3 of Simon Willison (see comments by Simon below).
7 Uses css selectors to apply javascript behaviours to enable
8 unobtrusive javascript in html documents.
13 'b.someclass' : function(element){
14 element.onclick = function(){
15 alert(this.innerHTML);
18 '#someid u' : function(element){
19 element.onmouseover = function(){
20 this.innerHTML = "BLAH!";
25 Behaviour.register(myrules);
27 // Call Behaviour.apply() to re-apply the rules (if you
28 // update the dom, etc).
32 This file is entirely BSD licensed.
36 http://ripcord.co.nz/behaviour/
43 register : function(sheet){
44 Behaviour.list.push(sheet);
48 Behaviour.addLoadEvent(function(){
54 for (h=0;sheet=Behaviour.list[h];h++){
55 for (selector in sheet){
56 list = document.getElementsBySelector(selector);
62 for (i=0;element=list[i];i++){
63 sheet[selector](element);
69 addLoadEvent : function(func){
70 var oldonload = window.onload;
72 if (typeof window.onload != 'function') {
75 window.onload = function() {
86 The following code is Copyright (C) Simon Willison 2004.
88 document.getElementsBySelector(selector)
89 - returns an array of element objects from the current document
90 matching the CSS selector. Selectors can contain element names,
91 class names and ids and can be nested. For example:
93 elements = document.getElementsBySelect('div#main p a.external')
95 Will return an array of all 'a' elements with 'external' in their
96 class attribute that are contained inside 'p' elements that are
97 contained inside the 'div' element which has id="main"
99 New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
100 See http://www.w3.org/TR/css3-selectors/#attribute-selectors
102 Version 0.4 - Simon Willison, March 25th 2003
103 -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
107 function getAllChildren(e) {
108 // Returns all children of element. Workaround required for IE5/Windows. Ugh.
109 return e.all ? e.all : e.getElementsByTagName('*');
112 document.getElementsBySelector = function(selector) {
113 // Attempt to fail gracefully in lesser browsers
114 if (!document.getElementsByTagName) {
117 // Split selector in to tokens
118 var tokens = selector.split(' ');
119 var currentContext = new Array(document);
120 for (var i = 0; i < tokens.length; i++) {
121 token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
122 if (token.indexOf('#') > -1) {
123 // Token is an ID selector
124 var bits = token.split('#');
125 var tagName = bits[0];
127 var element = document.getElementById(id);
128 if (tagName && element.nodeName.toLowerCase() != tagName) {
129 // tag with that ID not found, return false
132 // Set currentContext to contain just this element
133 currentContext = new Array(element);
134 continue; // Skip to next token
136 if (token.indexOf('.') > -1) {
137 // Token contains a class selector
138 var bits = token.split('.');
139 var tagName = bits[0];
140 var className = bits[1];
144 // Get elements matching tag, filter them for class selector
145 var found = new Array;
147 for (var h = 0; h < currentContext.length; h++) {
149 if (tagName == '*') {
150 elements = getAllChildren(currentContext[h]);
152 elements = currentContext[h].getElementsByTagName(tagName);
154 for (var j = 0; j < elements.length; j++) {
155 found[foundCount++] = elements[j];
158 currentContext = new Array;
159 var currentContextIndex = 0;
160 for (var k = 0; k < found.length; k++) {
161 if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
162 currentContext[currentContextIndex++] = found[k];
165 continue; // Skip to next token
167 // Code to deal with attribute selectors
168 if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
169 var tagName = RegExp.$1;
170 var attrName = RegExp.$2;
171 var attrOperator = RegExp.$3;
172 var attrValue = RegExp.$4;
176 // Grab all of the tagName elements within current context
177 var found = new Array;
179 for (var h = 0; h < currentContext.length; h++) {
181 if (tagName == '*') {
182 elements = getAllChildren(currentContext[h]);
184 elements = currentContext[h].getElementsByTagName(tagName);
186 for (var j = 0; j < elements.length; j++) {
187 found[foundCount++] = elements[j];
190 currentContext = new Array;
191 var currentContextIndex = 0;
192 var checkFunction; // This function will be used to filter the elements
193 switch (attrOperator) {
194 case '=': // Equality
195 checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
197 case '~': // Match one of space seperated words
198 checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
200 case '|': // Match start with value followed by optional hyphen
201 checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
203 case '^': // Match starts with value
204 checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
206 case '$': // Match ends with value - fails with "Warning" in Opera 7
207 checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
209 case '*': // Match ends with value
210 checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
213 // Just test for existence of attribute
214 checkFunction = function(e) { return e.getAttribute(attrName); };
216 currentContext = new Array;
217 var currentContextIndex = 0;
218 for (var k = 0; k < found.length; k++) {
219 if (checkFunction(found[k])) {
220 currentContext[currentContextIndex++] = found[k];
223 // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
224 continue; // Skip to next token
227 if (!currentContext[0]){
231 // If we get here, token is JUST an element (not a class or ID selector)
233 var found = new Array;
235 for (var h = 0; h < currentContext.length; h++) {
236 var elements = currentContext[h].getElementsByTagName(tagName);
237 for (var j = 0; j < elements.length; j++) {
238 found[foundCount++] = elements[j];
241 currentContext = found;
243 return currentContext;
246 /* That revolting regular expression explained
247 /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
248 \---/ \---/\-------------/ \-------/