March 21st, 2006

Introducing CSS event:Selectors

101 comments on 590 words

Over the past 6 months or so Javascript has really gotten a lot of attention. I can’t name a web application released in the previous months (although I’m sure there are a few) that doesn’t use Javascript to provide an enhanced experience for users. I wanted a way to facilitate that interaction that doesn’t involve me repeating myself over and over wiring and rewiring event ovservers to a document.

In steps Behaviour. It was the right script at the right time and it got people thinking about how easy it was to separate a lot (not all) of your inline event handlers (onclick, onmouseover, onmouseout, etc) from your html documents. It worked great for it’s purpose and is still a viable solution today, however, I still found myself writing a lot of code to perform common task.

I use Prototype in most, if not all applications I’m involved with today. Prototype mixes well with Behaviour, however, there is a lot of duplicate functionality between the two scripts and I’m still writing way to much Javascript. Here is a common example for those of you using Behaviour with Prototype:


  var rules = {
    '#item li': function(element) {
      Event.observe(element, 'click', function(event) {
        var element = Event.element(element);
        element.setStyle({color: '#c00'});
      });
    },

    '#otheritem li': function(element) {
      Event.observe(element, 'click', function(event) {
        var element = Event.element(element);
        element.setStyle({color: '#c00'});
      });
    }
  }

This is the proper way to wire up events using Prototype and Behaviour. It’s a lot of code just to make a few things on the page change their font color. Now here is that same example using event:Selectors:


 var Rules = {
    '#item li:click, #otheritem li:click': function(element) {
      element.setStyle({color: '#c00'});
    }
  }

As you can see event:Selectors uses almost the exact same syntax you just don’t have to write near as much code. You use pseudo event selectors to apply events. You can also use the same callback for more than one selector encouraging code reuse.

I’ve setup a page for this with more examples and documentation. You can also grab the download from there.

Click here to view examples, docs, and to download event:Selectors

Discussion

  1. Mike Mike said on March 21st

    Thank you for this. What a brilliant idea, treating events as selectors. After all, pure CSS already goes a few steps down the road with :hover, etc.

    I trust that this works with custom events, as well as standard?

  2. neville@bmsoft.com.au neville@bmsoft.com.au said on March 21st

    BTW, in your comparison with behaviour, the example could be reduced a little so that the difference in approach is less exaggerated. eg

    var element = Event.element(element); element.setStyle(‘color’: ’#c00’);

    can be reduced to:

    Event.element(element).setStyle(‘color’: ’#c00’);

  3. Justin Palmer Justin Palmer said on March 21st

    neville: In this simple case, your right. However, if you wanted to go beyond a single function call you would need a pointer to the element or the ability to chain methods.

  4. Alexander Alexander said on March 21st

    This seems great, working with prototype I have looked at behaviour, but I felt it was a little bit too much code for the fairly simple task, so I ended up writing loads of observers instead. But this seems interesting, I will definitly take a look.

    Also a more general comment about the blog; although you dont publish articles very frequently (nothing wrong with that) what you publish is always good quality, I have had great benefit from earlier articles such as enumerables and the binds. Quality before quantity – just as its supposed to be. Keep up the good work!

  5. topfunky topfunky said on March 21st

    Wow! Awesome.

    In the last year or two I’ve been on a slide of XHTML -> CSS -> XHTML auto-generation -> Javascript auto-generation -> CSS auto-generation (hopefully).

    This is an awesome tool in that chain and will make more complex intefaces easier to code.

  6. Mike Mike said on March 21st

    By the way, a tid-bit for those not in the loop like myself, this does seem to require the latest prototype 1.5 pre-release. It doesn’t work with the current stable 1.4.

  7. Justin Palmer Justin Palmer said on March 21st

    Mike, thanks for pointing that out. I kinda take it for granted that I’m hooked directly into the SVN repository. I’ll update the docs.

  8. Andrew Dupont Andrew Dupont said on March 21st

    Great work. The terseness of CSS syntax, when used in a JavaScript context, really highlights the bulkiness of selecting elements purely via the DOM. I think there will be more and more tools like this as designers take the plunge into DOM scripting.

    I have only one nitpick: if you need to selectively detach event listeners after the fact, you won’t be able to load any of them via this method. (This is more a weakness of Prototype and the DOM addEventListener method, in all fairness.)

  9. erik erik said on March 21st

    Something that would make this truly great would be support for attribute selectors in $$ (prototype selector).

    Currently one still need to use complex code when elements are selected on attributes (in forms this is common).

    Anyone know if attributeselectors is planned for the $$-function?

  10. Suthers Suthers said on March 21st

    Thought you might be interested in this little white-paper: Separating structure, presentation and behaviour.

  11. Dan Kubb Dan Kubb said on March 22nd

    Justin, this is very nice.

    By any chance does it support CSS2 style attribute selectors like input[type=text]?

  12. Sam Stephenson Sam Stephenson said on March 22nd

    Great job as usual Justin. Software evolution at work.

    For those asking about attribute selectors: no, Prototype’s Selector API doesn’t support those yet. Please do feel free to submit a patch. :)

  13. Michael Doubi Michael Doubi said on March 22nd

    Just tried it out, works like a charm!

    One great addition, as others have mentioned aswell, would be attributeselectors for the prototype selector api. I wish I had the skills to build a patch, but I am afraid I am a little to new to javascript. I think attribute selectors are available in \CSS query\ maybe someone with skills could use that technique and submit a patch, would be great!

  14. GDB GDB said on March 22nd

    I tried Behaviour and liked the concept, but the lack of integration with Prototype was a bit of a letdown. event:Selectors seemed to be the perfect solution, but alas, it suffers from the same thing Behaviour does: in IE, it can be very, very slow. In all fairness, though, this isn’t a problem with event:Selectors or Behaviour, but with IE itself.

    I’m working on a page that contains a table with appr. 30 rows. One cell in each row contains a checkbox, and I want to apply the same event to each checkbox onload. Since IE walks the DOM tree so friggin slow, it takes a good 30-60 seconds just to fully load the page.

    It’s a rather large table and the checkboxes are all over the DOM tree, so adding a root selector to force the browser to only look within that root element didn’t help.

    Anyway, I don’t wanna sound ungrateful, because event:Selectors can work great in the right situation. Just thought I’d add my 2 cents.

  15. Marc Brooks Marc Brooks said on March 22nd
    I’m no JavaScript whiz (still not sure about all that currying and such), so I’m probably showing myself to be an idiot, but… in event-selectors.js: assign method (line 87 in my copy), the line:
       if(pair[1] == '' || pair.length == 1) return rule.value(element);
    Couldn’t we make this slightly better as (using the assignment to event made two lines up):
       if(pair.length < 2 || event == '') return rule.value(element);
  16. Mr eel Mr eel said on March 22nd

    This is lovely! Less code is always better. Using psuedo selectors to specify events is a nice touch. Using compound selectors to attach events to elements going to reduce a lot of redundancy in my own code.

  17. topfunky topfunky said on March 22nd

    Also, you were linked to on WebCreme.

    Function AND style!

  18. Brian Donovan Brian Donovan said on March 22nd

    I just couldn’t resist the challenge: attribute selectors.

  19. Justin Palmer Justin Palmer said on March 22nd

    Brian: Haha! I was actually gonna play with that tonight. Great job. Sam, need any more patches? I have such eager and willing readers. ;-)

    @mark: Your point is valid. I did that as a last minute change when I was adding the ability to use non-event selectors and it slipped by me.

    @topfunky: Mint was so much faster than you ;-)

    @GDB: Safari has the same problem in regards to the speed of $$. The good news here is that it’s corrected in WebKit. Have you run any test in IE 7?

    Thanks for all the comments.

  20. Bramus! Bramus! said on March 23rd

    Excellent work and a nice derivation of behaviour!

  21. Alexander Alexander said on March 23rd

    Brian Donovan thanks alot, attributeselectors will make a good addition to $$. Beautiful, I am so happy now :P

  22. Tench Tench said on March 23rd

    Is one supposed to add EventSelectors.assign(Rules) in somewhere? event-selectors.js currently only assigns Rules as an Ajax onComplete responder. But how are Rules supposed to be initialized when the page loads for the first time?

    If I don’t add EventSelectors.assign(Rules)—nothing happens, but when I do, I get the TypeError – Value undefined (result of expression rules._each) is not object.

    I’m a bit confused. If anybody has any tips—please let know.

  23. Justin Palmer Justin Palmer said on March 24th

    Tench: You need to add EventSelectors.start(Rules) either before the closing body tag or in an onload event in an external JS file or in the head of your document.

  24. Kemar Kemar said on March 24th

    Why is it that in IE 6 on Windows on the test page the boxes when hovered over are not rounded corners but in compliant browsers it doesn’t. I this is suppose to degrade gracefully across all browswer platforms.

  25. Piotr Usewicz Piotr Usewicz said on March 24th

    Ah Justin, write more docs, I got the same problem as Tench.

  26. Piotr Usewicz Piotr Usewicz said on March 24th

    No… It does not work…

  27. Justin Palmer Justin Palmer said on March 24th

    Piotr: I commented right below Tenches comment on how you start the event:Selectors. I’ve also updated the docs page to reflect this and that you need Prototype 1.5.0_pre0 or greater.

  28. Piotr Usewicz Piotr Usewicz said on March 24th

    $$ undefined…

  29. Justin Palmer Justin Palmer said on March 24th

    Must….use….latest…version….of…..Prototype.

  30. Tench Tench said on March 24th

    Hey Justin, thanks a lot! Works like a charm.

  31. Piotr Usewicz Piotr Usewicz said on March 24th

    Yeah, it does! Thanks Justin!

  32. alexander alexander said on March 24th

    Brian Donovan: you dont happend to have a compiled version of prototype, with your patch included? (I dont understand the CVS-patch version).

  33. Tarellel Tarellel said on March 25th

    Hey Justin, Weh, this is amazing writeup, defiantly an addition to Prototype a lot of people should be looking at, and possibly using. It’s defiantly nice to see Dynamic behavior implemented into a site without a ton of spaghetti and excess garbage over-complicating the script.

    For those of you having issues with the script, I’m going to repeat what Justin has said numerous times: Use the Latest Prototype Release, it also helps if your trying this out with a decent browser, preferably Safari, Firefox, and/or Opera.

  34. Kyle Bradshaw Kyle Bradshaw said on March 26th

    This is great! Right now I’ve been doing a lot using Behaviour + Prototype and this seems like a great way to bridge the gap! Can’t wait to use it.

  35. Chris Williams Chris Williams said on March 28th

    I tried to use this with Form.submit events, and for some reason it does not work.

    Everything else works and thank you very much for it.

  36. Vasili Sviridov Vasili Sviridov said on March 28th
    Will i be able to this trick with event:Selector? Following code is for Behaviour.
    var pageBehaviour=
    {
       "a[href]" = function(el)
       {
           linkModifier(el);
       }
    }
    
    function linkModifier(link)
    {
       // do whatever stuff to link, not limited to :onclick and such
    }
    
    Just use it pretty much for detection of different elements in the page… Thanks.
  37. Mr eel Mr eel said on March 29th

    I’ve discovered a potential problem with event:selectors.

    How can I stop and event from bubbling? The most common use for this is stopping a link from loading. I may want to include a link with a href, so that it degrades nicely and have some JS that steps in when it gets clicked.

    Problem is, I haven’t found a way stop that from happening. Instead the JS executes and the link loads as normal. Any thoughts?

  38. Mr eel Mr eel said on March 29th

    Responding to Vasili’s post.

    You can acheive something similar, but the code is a little different.

    rules = { ‘a:loaded’: function(el) {el.hide()} }

    That would hide all the anchors on the page when they’re loaded. A silly example I know :)

  39. Justin Palmer Justin Palmer said on March 29th

    @Mr. eel: That isn’t bubbling, what your referring to is preventing the default action and stopping the propagation. This is fixed pretty easily:

    
    '#item a': function(element, event) {
      //do normal operations
      Event.stop(event);
      //Safari has a bug so we need to do this.
      // This bug is fixed in Webkit.
      element.onclick = function() { return false; }
    }
    
    

    That should get you going. :-)

  40. frobnicate.foo frobnicate.foo said on March 30th

    Really nice work!

    But I got a problem with the example above:

    var Rules = {
       '#item li:click, #otheritem li:click': function(element) {
         element.setStyle('color': '#c00');
       }
     }

    I’m getting the following error (both in IE and Firefox):

    missing ) after argument list element.setStyle(‘color’: ’#c00’);

    Seems like “setStyle” is not recognized … anything else (like a simple alert()) does work … and yes, I’m using the latest prototype-version…

    Any hints?

  41. Justin Palmer Justin Palmer said on March 30th

    Hey frobnicate.foo, I had an error in the code, it should be good now. Thanks for reporting it.

  42. Mr eel Mr eel said on March 30th

    “That isn’t bubbling,”

    Well, obviously I need to do a bit more reading about what bubbling is exactly. Thanks for setting me straight :)

    Also, thank you for taking the time to awnser my question. I really appreciate it.

  43. Dave Thomas Dave Thomas said on March 30th

    I want to have something like :

    var Rules1 = {
           '.adjuster:mouseover': function(element) {
    
             element.style.marginTop = '20px';
    
           }
        }
    
        var Rules2 = {
           '.adjuster:mouseout': function(element) {
    
             element.style.marginTop = '0px';
    
           }
         }
    
        EventSelectors.start(Rules1);
        EventSelectors.start(Rules2);
    

    How could I do that

  44. Dumbledore Dumbledore said on March 30th

    it´s extremly slow when i a a rule for a class like:

    '.input_180:focus': function(element){
        element.style.border='1px #E20074 solid';
    },

    The Internet-Explorer crasht, and the Firefox sends a timeout. With behaviour i have no problem…

    Any ideas?

  45. frobnicate.foo frobnicate.foo said on March 31st

    @Justin Palmer: Still got the same problem. I re-downloaded event-selectors.js (did you actually change anythning?) as well as prototype.js from the SVN-repository … maybe I’m dumb, but looking at the prototype.js (1.5.0_pre1 it says) ... would’nt it rather have to be “setStyle(element, style)” than “setStyle(element : style)” ... Or am I really missing the point here?

    @Dumbledore: Same problem here, Firefox gives a timeout (offers me to stop the script) and IE hangs for 30 secs or so … Windows XP, FF 1.5.0.1, IE 6

    Thanks for your help Justin, I’m really looking forward to regularly using your nice work ;-)

  46. pascal pascal said on April 1st
    In Behaviour you can use { ".foo" : function(){ this.hide(); } } which I personally prefer… Could you add this to event:Selectors, too?

    And the ability to just stop bubbling by returning false? Like { "a:click" : function(e,evt){ return false; }

    This would make me love this script :D

  47. Justin Palmer Justin Palmer said on April 1st

    pascal: about seven comments up I described your second problem and how to properly handle it.

    As far as your first issue, you can do this except you don’t use the `this` keyword:

    1
    2
    3
    
    '.foo': function(element) {
       element.hide();
    }
  48. Justin Palmer Justin Palmer said on April 3rd

    Hey guys, I just wanted to say that if you believe you have found a bug or problem (like the crashing problem above) then please file a ticket here: http://encytemedia.com/treasure/newticket.

  49. What Silence What Silence said on April 3rd

    Just wanted to say “Cheers”. This is a weblog that I keep finding myself coming back to (via Ajaxian, mostly)... finally subscribed to the feed today. ;)

    This is definitely an elegant piece of code. Thanks for making it available.

  50. Rob Rob said on April 5th

    This may be a bit obvious but in addition to the options the documentation mentions to load the rules (via body onload=”” or sticking a script section at the end of the file) you can also do this:

    Event.observe(window, ‘load’, function() { EventSelectors.start(Rules) });

    Which has the advantage of compatibility with any other onload events your page may already have.

  51. robert liu robert liu said on April 7th

    is there an alternative approach to simplify “apply rules”? in other words,a browser compliant way to apply rules on window onload,so I can write eventSelectors.apply(rules) anywhere,e.g. in mycustom.js.

  52. robert liu robert liu said on April 7th

    I tried it just now. good work! I found a bug:

    if “TABLE TR:mouseover” is applied,the “element” seems to be TD(wrongly) instead of TR.

    code:

    var rules = { TABLE TR:loaded){ }, TABLE TR:mouseover){ element.style.backgroundColor = ”#ECEC00”; // element.innerText = element.tagName; }, TABLE TR:mouseout){ element.style.backgroundColor = ”#ECECEC”; } }

    EventSelectors.start(rules);

    BTW,I asked a stupid question just now. Rob told me the answer,thank all of you.

  53. robert liu robert liu said on April 7th

    more: “TR:loaded” will run the js function many times instead of once. when I change “TR:loaded” to “TR”,it is ok.

  54. robert liu robert liu said on April 7th

    I should learn more before speak,but I can’t help :) I looked at the code,neat! loaded event really use “setInterval” but “clearInterval” doesn’t works for me (strange).

    another issue found: (I always have many questions :)

    prototype’s $$ function doesn’t support “css attribute selector” yet, e.g.: INPUT[type=”text”]

    but behaviour now support. and if I change behaviour.js :169 from: if (token.match(/\[(\w+)([=\|\\$\]?)=?”?([\]”])”?\]$/)) { to: if (token.match(/(\w)\[([\w\:]+)([=\|\\$\]?)=?”?([\]”])”?\]$/)) { it also support this style(doubt whether it follows W3C standards) INPUT[mynamespace:myattr=xxx]

    imagine its power: describe data and presentation as zpt(inspired by zope) style,and bind them(and their behaviour) automatically.

  55. Justin Palmer Justin Palmer said on April 7th

    Robert liu: Make sure your on the latest version of Prototype as the post suggest and you will have attribute selectors.

  56. robert liu robert liu said on April 7th

    “attribute selector” is discussed before:)

    I tried behaviour selector with ”*”, IE throws exception.

    thanks and I’d study now.

  57. robert liu robert liu said on April 7th

    I am so excited today,and thank Justin Palmer for your kindness. prototype in SVN has “attribute selector)), selector.substring(selector.lastIndexOf() ]; }else{ pair = [selector]; }

    amazingly it works~ and the above comment I wrote is not about selector ”*”,but a IE bug: http://mail-archives.apache.org/mod_mbox/beehive-dev/200503.mbox/%3C1644874402.1112221182516.JavaMail.jira@ajax.apache.org%3E [quote] in IE calling getAttribute(name) with a name space qualified name “ns:name” results in an exception being thrown if the DOM element is a table. [/quote]

    so when I write
    • or TABLE[ns:attrname=attrvalue], and table has not the attribute, IE throws exception. we can follow what the maillist says: put a try catch around the calls to getAttribute() which eats the exception if it’s thrown. (and return null?) I think we can put the slice to somewhere of prototype…

    forget my poor English and rude,thanks for your good work,I am enjoying it! table.getAttribute..}catch(e){} can ig

  58. NextOne NextOne said on April 10th

    Hi, I’ve add some usefull stuff to allow any script loaded to append their own rules:

    reload: function(){ this.assign(this.rules); },

    register: function(newRules){ for (var property in newRules) { this.rules[property] = newRules[property]; } },

    - So now start() do not have parameter - Ajax.Responders call reload() - EventSelector load at startup:

    Event.observe(window, ‘load’, function() { EventSelectors.start(); });

  59. robert liu robert liu said on April 11th

    I’d say sorry ahead for I still have no idea how to format code snippet when post on the blog.

    report a bug: html: <input><input> js: var rules = { ‘INPUT:loaded’: function(element){ element.value = “test”; } }; browser:IE the two INPUT are set value to “test”,but when we focus on the first one,and try to input something,it always change back to “test”. setInterval is not cleared.

    see [css event selectors] code: this.timer[pair0] = setInterval(this._checkLoaded.bind(this, element, pair0, rule), 15);

    we set interval’s id as pair0,which is selector name(for now,’INPUT’),so when we setInterval twice with the same name,and then clearInterval twice with the same name as above,what happens?

    It seems Firefox clearInterval will destroy the two one by one,it is ok. but IE will always find the latest interval and try to destroy it again and again. (the other intervals will live and always execute so that it change back what I input)

    we should set interval’s id corresponding to element.id. but I’ve no idea how to simulate IE’s uniqueID property for FF,etc. so I add a piece of ugly fake code to let it work:

    ”””>>> Object.extend(Element.Methods,{ “getUniqueID”: function(element){ if(element.id){ return element.id; }else if(element.uniqueID){ return element.uniqueID; }else{ var elementId = ”” + Math.random(); element.id = elementId; return elementId; } } });

    <<<”””

    and change the above line to : ”””>>> var elementId = element.getUniqueID(); this.timer[elementId] = setInterval(this._checkLoaded.bind(this, element, elementId, rule), 15); <<<”””

  60. robert liu robert liu said on April 11th

    I feel upset for giving u much trouble on the page format. I’ve post my previous notes on my blog,can u move there to check?

    http://blog.donews.com/liusong1111/archive/2006/04/12/823984.aspx

    thanks.

  61. Justin Palmer Justin Palmer said on April 11th

    Hi Robert, it’s no problem, however it’s better that you report your bugs and enhancement suggestions through the proper channels. Here is the place to submit your ticket.

    event:Selectors is Open Source and by using the bug tracker, it gives other folks besides myself the opportunity to take a look at what you’ve got and maybe supply a fix or properly formatted patch.

    @somedude: IE and Safari both are really slow using the $$ sign. This is more of a browser issue than one directly related to Prototype. The good news is that Webkit fixes Safari’s speed issues, however I’m not sure about the progress of IE. I would like to do some benchmarking and testing to see if it can be sped up any though.

  62. tigglet tigglet said on April 13th

    I’m a bit of a newbie to javascript and css but I’m learning in leaps and bounds! Behaviour was a bit of help in introducing me to some of the concepts I needed to understand. Now I’m trying to use event:Selectors and like what I see so far. But I’m having an issue I hope someone can help me with.

    I’m trying to get event:Selectors to work with a DHTML goodies tooltip script and having no luck. The script is here: http://dhtmlgoodies.com/index.html?whichScript=tooltip_shadow

    My rules are defined as follows:

    var Rules = {
        '#toolTip:mouseover': function(element) {
            showTooltip(element,'This is my tip');
        },
        '#toolTip:mouseout': function(element) {
            hideTooltip();
        }
    }

    I keep geting an error in the dhtml_goodies script about not being able to parse the left or top values. As near as I can figure, I’m not passing in the correct object to the showTooltip() function. Can someone help me figure out the syntax to get this working properly?

    One other question: Is there a list serve or official discussion board I can join to ask these type of questions?

    Thanks in advance!

  63. Justin Palmer Justin Palmer said on April 13th

    Hi tigglet: You can signup for the Rails Spinoffs mailing list: http://lists.rubyonrails.org/mailman/listinfo/rails-spinoffs to discuss event:Selectors and get help.

    I’m not 100% positive because I’m not sure what dhtml script your using or what your html looks like, but my guess would be that:

    a) You have more than one id of toolTip which is invalid b) Your toolTip container is hidden and is the tooltip itself, you need to use the mouseover on an element that’s visible and one which triggers the real tooltip.

  64. tigglet tigglet said on April 14th

    Justin: thanks for your answer. I was able to solve the problem. It turns out that I needed the event object and all I was sending in was the element object. For anyone else:

    Rules = {
        '#toolTip:mouseover': function(element, event) {
            showTooltip(event,'This is my tip');
        },
        '#toolTip:mouseout': function(element) {
            hideTooltip();
        }
    }

    Note the addition of the second parameter named “event” in the mouseover function. This is documented on the event:Selectors page but it took one of my colleagues pointing it out to me for me to really read it and understand what it signified.

    Thanks again for your help!

  65. Les Szklanny Les Szklanny said on April 19th

    Love the concept… do you have an estimate when this (major) bug will be fixed?

    mouseover- and mouseout-events cannot be applied to table-row

    http://encytemedia.com/treasure/ticket/3

  66. Adam Messinger Adam Messinger said on April 19th

    This is great stuff, but has the downside of requiring the 50+ KB Prototpye library.

    I recently ran across something roughly similar in function to what you describe here, but the JavaScript involved is much slimmer: moo.dom, by Mad4Milk. The moo.dom.js script weighs in at just under 3 KB, and the pared-down version of Prototype that M4M uses in all their stuff is just over 3 KB.

    It’s worth checking out for those who want an easy way to add JavaScript events using simple CSS selectors, but don’t need the full functionality of Prototype.

  67. Arthur Arthur said on April 25th

    I’ve got a question: would it apply event to returned by Ajax html? In my case it is not observing event (for example click) when i click returned by this library html code. Is it normal? Beg you pardon if this question is stupid but this is my first time expirience with js.

  68. allan allan said on April 29th

    Nice stuff wish I could use it Unfortunetly it unusable from a practicle point of view as it adds a good 30seconds to my page load times!! :( :(

    I’ve no fancy rules, its just the initialization load. I was trying this on a phpbb discussion bord. Perhaps the use of $$() (mentioned above as slow) is worse in that situation.

    Bummer.

  69. allan allan said on April 29th

    Not sure if this should be reported as a ‘bug’ but the examples, demo web page says your start this up with this: eventSelectors.start(Rules); and I had to use EventSelectors.start(Rules);

    Capital ‘E’

  70. Tim Tim said on May 2nd

    I’m having trouble getting event-selectors to trigger an event if the source is bubbled up.

    ex. <div class="test"> <a href="test.htm"></a> </div>

    I want an event triggered in the anchor tag to trigger the event in the div tag which is assaigned using event-selectors.

    any clue how to do this? I can’t get it to work.

  71. Tim Tim said on May 2nd

    soryy about the last comment, it was a programatic error on my part.

    Thanks.

  72. Taylor Taylor said on May 2nd

    When you want to apply new rules to the current page. for example: a fairly complex ajax dhtml online app where there are many different event has to be fired. you can have different set of rules to be appied in different fashion.

    1. be default: if you apply a new rule, the existing rules/events will be overwrited and for some reason it will not stay.

    --—- var Rules1 = { ... } EventSelectors.start(Rules1); var Rules2 = { ... } EventSelectors.start(Rules2);

    2. in order to let rules1 stay in above example. you can do following. (use prototype’s object extend to extend the set of Rules)

    var Rules = { ‘ul li:click, h2:loaded, h1:mouseover’: function(element) { element.setStyle({color: ’#c00’}); } };

    EventSelectors.start(Rules);

    var newRules = { ‘h1:loaded’: function(element) { element.setStyle({color: ’#fff’}); } };

    Object.extend(newRules, EventSelectors.rules);

    EventSelectors.start(newRules);

    I found it is easy to switch between above examples depending what you need. if different class does different events, it could be helpful to use the Object.extend

    Otherwise use the first one to overwrite old one.

  73. Cadmium Cadmium said on May 16th

    I’ve had the same problem as Alan with the example page.

    I copy/pasted “eventSelectors.start(Rules);” into my code and that didn’t work because of the first ‘e’, which should have been capitalized. Didn’t take me too long to figure it out, but it would be nice to see it changed on the example page to prevent others from making the same mistake.

    Also, for some reason, IE doesn’t seem to cope well with the following rule:

    '.header:click': function(element) {
      ...
    }

    whereas it works fine in Firefox (IE gives the usual ‘object doesn’t support property or method’). Adding an id-attribute to the element in question and changing the rule like this:

    '#header:click': function(element) {
      ...
    }

    seems to work fine in both browsers (although that’s not what I really wanted ;o)).

    Other than that, it seems to work fine!! Thanks for this great peace of code, Justin!! I think you’ve made a lot of people happy. Keep up the good work!

  74. Cadmium Cadmium said on May 16th

    The latter problem turned out to be a bit weirder than I thought. The following rule (from event-selectors.js) and html will fail in IE only:

    var Rules = {
    
      '.header:click': function(element) {
        alert(element.tagName);
      }
    }
    
    <html>
    <head>
        <script type="text/javascript" src="prototype.js"></script>
        <script type="text/javascript" src="event-selectors.js"></script>
    </head>
    <body>
    
    <form method="post">
        <div class="panel">
            <div id="header" class="header">
                <input name="toggle"/>
            </div>
        </div>
    </form>
    
    <script type="text/javascript">
        EventSelectors.start(Rules);
    </script>
    </body>
    </html>
    

    But either removing the form, changing the input name from ‘toggle’ to something else or changing the rule selector to id will make it work fine.

    Anyone :o)?

  75. subversion subversion said on May 24th

    First of all, great addition. This finally makes some things a lot more useful without having to hack around with multiple scripts using cssQuery.

    But, on to my question: any ideas on how to use .cancel() with this? For example: I am playing with this on a list. Each li has a link followed by an inner div that contains a description of the link. Mouseover the link, it expands the descriptive div, mouseout, it closes the div.

    What happens is that if one were to mouseover the list very quickly (from top to bottom), one would get all of the events daisy-chained together. Kind of a slow moving accordian (especially since the links keep moving if one wanted to click on it), or the potential for a user to give themselves a bad experience.

    So far I haven’t been able to get anything good working, and I was just wondering if anyone had any ideas on this?

  76. Tobie Tobie said on May 28th

    Hi Justin,

    Thanks a lot zillions for a great fantastic piece of work!

    Just added a (small) bug report and patch here.

    BTW, your trac seems to have been nastily spammed recently.

  77. Luc Luc said on June 21st

    var Rules = {

    '#detail:mouseover': function(element)   {
      alert('detail');
    },

    }

    aaaa bbbb

    when I mouse over the “bbbb”, nothing happen. It only works for “aaaa”

    anyone know why?

  78. Flip Sasser Flip Sasser said on June 25th

    Element:loaded rules appear to break by causing an infinite loop if there is more than one of that type of element on the page. For example:

    “textarea:loaded”: function(element) { alert(element); }

    Creates an infinite loop. ?

  79. Flip Sasser Flip Sasser said on June 25th

    Okay I’ve figured it out and solved a small bug in the way it works. First, in this code block:

    @assign: function(rules) { var observer = null;@

    I’ve added: var x = 0;

    And now, when we’re adding to the timer array, if we have more than one textarea object, the timer overwrites the old setInterval with another this.timer[timer] item, basically creating many an unregulated setInterval that repeats itself endlessly. So we just have to make the array keys unique, like so:

    @$$(pair0).each(function(element) { if(pair1 '' || pair.length 1) return rule.value(element); if(event.toLowerCase() == ‘loaded’) {@ x++; this.timer[pair0 + x] = setInterval(this._checkLoaded.bind(this, element, pair0 + x, rule), 15);@

    This makes sure we don’t overwrite previous setIntervals in the array by keeping each key unique, even if we have two textarea:loaded rules. And now we can perform automatic transformations on textareas – like replacing them w/a WYSIWYG editor – no matter how many are loaded. Huzzah!

  80. Anonymous Anonymous said on July 3rd

    Re: Child element being passed to handler?

    Why is it preferrable to pass the child element?

  81. Anonymous Anonymous said on July 10th

    In regards to the previous message (see mailing list link above), it appears this is a bug, a serious one IMO.

  82. chris chris said on July 13th

    Hi there,

    just tried to run event:selectors.

    i just installed the two latest versions of ES and prototype.

    But i get this error from the js console:

    “Object.extend is not a function”

    Any ideas?

    Best regards, chris

  83. Chris Chris said on July 13th

    Problem solved. The problem lied within the rake made prototype.js.

    Things work just fine now ;-)

  84. sam sam said on July 13th

    Problem:

    As strange as it seems, “element.id” does not return the elements’ id the css selector is assigned to but the nested one.

    Please see the following code snippet:

    javascript:

    var Rules = { ’.removeItem:click’: function(element) { var id = element.id; alert(id); new Effect.Fade(id, { afterFinish: function(element){alert(‘effect end’);} },{duration: 0.4}); } }

    html snippet:

    “alert(id)” returns “foo” and not “anchor”,as one would expect.

    Shouldn’t the returned id be the one of the element with the assigned class???

    Thanks in advance, chris

  85. sam sam said on July 13th

    sorry,forgot to escape ;-)

    html snippet: <div> <a href=”#” id=”anchor” class=”removeItem”> Foobar</span> </a> </div>

  86. sam sam said on July 13th

    ....

    <div> <a href=”#” id=”anchor” class=”removeItem”> <span id=”foo”>Foobar</span> </a> </div>

  87. Drixer Drixer said on July 26th

    Thanks for this great bit of code. It has been linked to from my site TipClique Tutorials, and I think once my site gets some visitors many will find their way here to find help for this nuisance.

  88. Beatle Beatle said on August 17th

    I have been using behaviour.js and when i came across ES is specially liked its utility in reducing the duplication of selector functions. However i could not get it working.

    Tracing through event-selectors.js, i found the code execution stopping at the line $$(pair0).each(function(element) {....

    I have no knowledge how $$ is used, could anyone help me out with this?

  89. pete pete said on September 7th

    great work, justin, I have one question for you, how can I pass a parameter to the function

    conventional event handling like this:

    click for event

    now, I use event selector:

    click for event

    var Rules = { ’#nv div:click’: function(element,event){ testevent(element,event); } } function testevent(element,e){ }

    how can I pass the “myvalue” to the event-selector function? Thanks.

  90. Thomas Traub Thomas Traub said on September 26th

    Please help me !

    How to start the event:Selectors ?

    I’ve tried the basic syntax part of the page What is it?

    but I get always the error : EventHandler is not defined!

    I’ve tried all possible variations (mouseover, OnMouseOver, event:mouseover, ...) but I just can’t figure it out. This way of juggling javascript is new for me, and it’s really hot, if I could only get me going.

    My code of the head part:

    <script type="text/javascript" language="JavaScript">
    // <![CDATA[
      var Rules = {
        '#icons a:mouseover': function(element) {
        var app = element.id;
        new Effect.BlindDown(app + '-content', {queue: 'end', duration: 0.2});
        }
      }
    // ]]>
    </script>
    

    My code before the closing body tag

    <div id="#icons">icons</div>
    <script type="text/javascript" language="JavaScript">
    // <![CDATA[
      EventSelectors.start(Rules);
    // ]]>
    </script>
    

    Thanks a lot ! Thomas

  91. Jaime Martinez Jaime Martinez said on September 27th

    Hi there! I would like to work with this functionality provided by event:Selectors but I couldnt find a download-link. Could this be cuz of the redesign? I’m looking forward to be working with this. Thanks in advance.

    Jaime!

  92. alexandre van de sande alexandre van de sande said on October 3rd
    Ok, I feel really stupid asking this, but I am in the arid desert of the initial learning curve and feeling utterly confused. First: where are the pages, the documentations and the downloads you all are talking about. The only link I could find was for the bug report, and it's giving a 404 error. Second: what should I download to be able to use event selectors? If I have the last version of scriptaculous I can start using all those nice nifty examples? Or is there a "event selector" patch for prototype? third: I am confused on where should I be putting all the codes. So I have to have a <head> describing what the rules and then a EventSelectors.assign(Rules) just before the <body> tag? Gee, can I have a downloadable demo or a copy paste ready thing? fourth: thanks for all your useful contributions for the web... alex - rio de janeiro
  93. Ben Surgison Ben Surgison said on October 5th
    In answer to: Thomas Traub September 26th Add the following to your imported javascript: // Auto load rules as soon as the document body exists. function loadRules(){ if(document.body==null) setTimeout("loadRules()", 10) else EventSelectors.start(Rules); } loadRules();
  94. Andy Blackwell Andy Blackwell said on October 13th
    Hi Justin, had some troubles getting events to stop, and came along this tip that fixed it all. When I put Event.stop(event) at the end, it does not work..links are still followed. '.links a:click': function(element, event) { $('item').toggle(); Event.stop(event); } But when I put it first, it works for me.. '.links a:click': function(element, event) { Event.stop(event); $('item').toggle(); } I had to rewrite some 'if' statements so it would execute in the first scenario, rather than in the 'else'. Hope this might help another head scratcher like me!
  95. Andy Blackwell Andy Blackwell said on October 13th

    Hi Justin, had some troubles getting events to stop (like links), and came along this tip that fixed it all. When I put Event.stop(event) at the end, it does not work..links are still followed.

      '.links a:click': function(element, event) {
        $('item').toggle();
        Event.stop(event);
      }
    

    But when I put it first, it works for me..

      '.links a:click': function(element, event) {
        Event.stop(event);
        $('item').toggle();
      }
    

    I had to rewrite some 'if' statements so it would execute in the first scenario, rather than in the 'else'. Hope this might help another head scratcher like me!

    I hope this comment is formatted right this time. *crosses fingers*

  96. Yeago Yeago said on October 16th
    This is a wonderful thing. synthesizing cssey syntax with js is neato and genius. Some problems I had: 1) this discussion page is not linked to the project page. highly disorientating. 2) perhaps its all the black on gray text, but I did not see "THIS ONLY WORKS WITH PROTOTYPE 1.5" (except here, to the many many people wondering why their script doesn't work. when many people ask, there's probably a reason for the trend). 3) Unrelated: the WHERE THE #(*(#&! is Prototype 1.5 available for download?!?!?! Rabbithole. Short version: WhrTFisPRTTYP1.5??? Ended up downloading Scriptaculous, and pulling its prototype.js out for use. -- All in all, I wanted to get rip roaring in 5 minutes and it took me upwards of an hour because of the poor connectivity (....strange, I thought that's what you posh 2.0ers were all about =)
  97. Leo Leo said on October 30th
    Hi Justin. Great work on event-selectors.js I downloaded it back in September, and at that time found some bugs and short comings. I'd read through the postings others had made, and believe fixed most of them. I've looked for the download link to see if you've updated your code since, but I can't find a link as some one else has previously mentioned. Also the link to the bug tracker is dead. So I'll post my comments and a link to my modified version here. http://www.guildmedia.net/js/event-selectors.js Here are the issues and bugs: 1. event-selectors have a minor problem when using attribute selector with namespace like this: INPUT[myns:myattr=myvalue] the selector.split(’:’) is now replaced with a regex. 1. added this methods 1. apply: – while there exists code to re-apply the rules after the ajax call, there is nothing if for when the DOM is modified without Ajax. 2. addRules: – extend the rule set. 3. addLoadEvent: – No longer need inline code in the body or for onload() to EventSelectors.start() !!! After all that is the idea of the CSS Event Selectors ;-) 4. register – 1. Nasty bug – ’.artistThumb:click’ : function(element, event){ Will call the function, but the element passed to the function is missing its id, so you can’t do element.id The observer function is now: observer = function(event) { var element = this; //Event.element(event); It now correctly passes the element with its id intact. The full licensing details in the js script file is to bulky for a production environment. I replaced this with a url.
  98. Justin Palmer Justin Palmer said on October 30th
    Great work Leo, do you mind if I include your changes in a new release?
  99. Anonymous Anonymous said on November 2nd

    Changes look great Leo, I would move from tabs to two spaces for people diffing can understand what you’ve changed more easily.

  100. Leo Plaw Leo Plaw said on November 2nd

    Hi Justin.

    By all means put my changes to use. You shared your code with us all, so I’m more than happy to give back. Thanks also goes to the people who posted their bug reports.

    Keep up your good work!

  101. Leo Plaw Leo Plaw said on November 6th

    Hello Anonymous.

    I use tabs because it makes for a smaller file size in a production environment. You could use a tool such as UltraEdit to convert the tabs to spaces, and then do your diff.

    Or wait until Justin releases the next verions with the changes.

    BTW, Justin, can you let me know when you do please?

    Or Anonymous, if you’re really wanting the tabs to spaces I can post a copy up on my site as well.

Sorry, comments are closed for this article.