Welcome!  You'll find it a bit of an adjustment moving from jQuery to
Prototype, but there's a lot of good stuff to get into.  I'd recommend
having a good, thorough read through the API docs[1] to get started
with, just so you know what's available to you.  Takes about an hour,
but pays you back dramatically in saved time.

One of the biggest things you'll need to get used to is that Prototype
does not conflate the concepts of an Element and an Array or list --
they're clear and distinct concepts.  jQuery does a lot of that kind
of conflation, which some like and others don't.  So the methods that
are available on Element (like addClassName and removeClassName) are
not available on Array, you must explicitly walk through the array --
although Prototype provides a lot of nifty stuff to help you do so.

I can't comment on anything specific to Event.addBehavior since I've
never used that plug-in (that's not Prototype, it's an add-on -- Low
Pro, I think).  But there are some errors in the code I can help with
that I'm reasonably sure don't relate to that part of things:

> <div id="submit-choices'>

I'm guessing that you typed in the HTML structure, rather than copy-
and-paste?  You're opening with " there, but closing with '.

>   Event.addBehavior({
>     '#submit-choices label:click' : function() {
>         removeClassName($('submit-choices div'), 'active');
>         this.addClassNAme('active')
>       }
>     }

A couple of issues there:

1. You're using removeClassName as an unqualified reference, and so it
will be resolved relative to the scope chain.  In the code you've
quoted, I don't see a containing scope that will have a
removeClassName function.  You probably want Element.removeClassName,

2. ...you're using $() and giving it a selector-like string ("submit-
choices div"), although without the # at the beginning.  $() is purely
for looking up elements by ID[2].  If you want to find elements by
selector, you want $$()[3] to do it at the document level, or
Element#select[4] to do it within a given element (e.g., searching its

>       var divs = document.getElementById('submit-
> choices').getElementsByTagName('div');
>       if ( divs.hasClassName('active' )) {
>         divs.removeClassName('active');
>         this.addClassNAme('active')
>       }
>     }

Here's that conflation problem.  You're setting 'divs' to a NodeList,
then calling a method from Prototype's Element extensions on it.
NodeLists aren't Elements, they're NodeLists.  You want to operate on
each item in the list.  In Prototype, if the goal is to remove
'active' from all divs within submit-choices and then add active to
the label on which the click occurred (assuming Low Pro hooks up
handlers using Element#observe), this would do it:


That uses $()[2] to look up the submit-choices div, then Element#select
[4] to get an array of its decendant divs, then Enumerable#invoke[5]
to look through that array calling the 'removeClassName' method on
each element and passing in 'active' (Element#removeClassName is okay
with being called when the class name isn't on the element; it's just
a no-op).

I'd probably approach the whole problem differently, though.  If the
goal is that clicking a label will mark its parent div as "active" and
clear any other active divs, and that all of this happens within a
container called 'submit-choices', I would probably put one single
handler on submit-choices rather than using Low Pro to hook up every
label within it.  Clicks bubble up, so a click on the label will be
seen by submit-choices barring someone stopping it.  So this would be
all that you needed:

* * * *
document.observe('dom:loaded', function() {
    $('submit-choices').observe('click', function(event) {
        var label;
        var div;

        label = event.findElement('div > label');
        if (label) {
            div = label.up();
* * * *
(Also on Pastie: http://pastie.org/459429)

That one handler, on submit-choices, looks to see if the click was on
a label with a div as an immediate parent.  If it was, it will remove
the "active" class from all "active" divs within the container, then
add the "active" class to the parent.  Key bits of the above are
document.observe's dom:loaded event[6], the fact that Element#observe
[7] ensures that "this" within the event handler refers to the element
on which the event is hooked (so "this" is submit-choices within th
ehandler), and Event#findElement[8].  I think nowadays findElement
actually accepts a full CSS selector, but the docs for it still just
say "tag name" so I haven't relied on that above; instead I explicitly
check the parent's tag name.

You can add and remove divs within the submit-choices div, and they'll
get picked up by the handler above -- they don't need to get anything
hooked on them.  The only time you'd have to unhook/rehook a handler
would be if you took out submit-choices itself.  This is generically
called event delegation and can be very powerful when dealing with
lists of things, particularly dynamic lists of things.

[1] http://prototypejs.org/api
[2] http://prototypejs.org/api/utility/dollar
[3] http://prototypejs.org/api/utility/dollar-dollar
[4] http://prototypejs.org/api/element/select
[5] http://prototypejs.org/api/enumerable/invoke
[6] http://prototypejs.org/api/document/observe
[7] http://prototypejs.org/api/element/observe
[8] http://prototypejs.org/api/event/findElement

T.J. Crowder
tj / crowder software / com
Independent Software Engineer, consulting services available

On Apr 27, 5:50 am, Jeff <rufus2...@gmail.com> wrote:
> i'm new to prototype. i was using jQuery and loved it and am a little
> confused coming over to prototype. i am trying to add and remove a
> class of active to a labels parent div. does anyone know why i can't
> do this?
> i've tried this
> <div id="submit-choices'>
> <div>
> <input /><label>my label</label>
> </div>
> <div>
> <input /><label>my label</label>
> </div>
> </div><!-- close submit-choices div -->
>   Event.addBehavior({
>     '#submit-choices label:click' : function() {
>         removeClassName($('submit-choices div'), 'active');
>         this.addClassNAme('active')
>       }
>     }
>   })
> and also
>   Event.addBehavior({
>     '#submit-choices label:click' : function() {
>       var divs = document.getElementById('submit-
> choices').getElementsByTagName('div');
>       if ( divs.hasClassName('active' )) {
>         divs.removeClassName('active');
>         this.addClassNAme('active')
>       }
>     }
>   })
> it keeps saying divs.hasClassName is not a function. i mean wtf?
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to prototype-scriptaculous@googlegroups.com
To unsubscribe from this group, send email to 
For more options, visit this group at 

Reply via email to