behaviors context

View: New views
14 Messages — Rating Filter:   Alert me  

behaviors context

by David Cohen-8 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I'm trying to implement a Drupal.behavior.  I realize my behavior
function is passed a "context".  But it's unclear to me what that
context will be.

If I understand correctly, when the behavior is first invoked, it is
passed document, a variable built into javascript.

Later, when some module's javascript adds content to the page, my
behavior will be invoked again.  This time, context could be anything.
The module which added the content can pass whatever.  So for example in
ahah.js, Drupal.attachBehaviors() is passed a jquery object representing
the new content.  While in modules/views/js/ajax.js,
Drupal.attachBehaviors() is passed a string or id, either of which could
be used in a jquery selector.

Do I have this right?

What I need is the javascript element (not jquery object) that is the
added content.  Does anyone have a snippet of code which returns this,
given a context?

Thanks, -Dave


Re: behaviors context

by Matt-192 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

As far as the context goes, yes -- you are at the mercy of whatever
called Drupal.attachBehaviors(). So it's not all that reliable.

Typically, the whole CSS :not() pseudo-class  combined with a
'processed-' class is used to help you find segments that have not
been processed already. There are ample examples of this inside of
Drupal's JS libraries.

Matt

On Fri, May 29, 2009 at 12:36 PM, David Cohen <drupal@...> wrote:

> I'm trying to implement a Drupal.behavior.  I realize my behavior
> function is passed a "context".  But it's unclear to me what that
> context will be.
>
> If I understand correctly, when the behavior is first invoked, it is
> passed document, a variable built into javascript.
>
> Later, when some module's javascript adds content to the page, my
> behavior will be invoked again.  This time, context could be anything.
> The module which added the content can pass whatever.  So for example in
> ahah.js, Drupal.attachBehaviors() is passed a jquery object representing
> the new content.  While in modules/views/js/ajax.js,
> Drupal.attachBehaviors() is passed a string or id, either of which could
> be used in a jquery selector.
>
> Do I have this right?
>
> What I need is the javascript element (not jquery object) that is the
> added content.  Does anyone have a snippet of code which returns this,
> given a context?
>
> Thanks, -Dave
>
>



--
http://technosophos.com
http://querypath.org

Re: behaviors context

by Nedjo Rogers-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

David Cohen wrote:
> I'm trying to implement a Drupal.behavior.  I realize my behavior
> function is passed a "context".  But it's unclear to me what that
> context will be.

The context is supposed to be the element to be processed--not a jQuery
object but the page element. See the 5 - 6 upgrade notes:
http://drupal.org/node/114774#javascript-behaviors

However, this fact may have been lost along the way.

Re: behaviors context

by Daniel F. Kudwien :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> What I need is the javascript element (not jquery object)
> that is the added content.  Does anyone have a snippet of
> code which returns this, given a context?

If I'm not mistaken:

var new_content = $(context).get(0);


sun


Re: behaviors context

by Daniel F. Kudwien :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> The context is supposed to be the element to be
> processed--not a jQuery object but the page element. See the
> 5 - 6 upgrade notes:
> http://drupal.org/node/114774#javascript-behaviors
>
> However, this fact may have been lost along the way.

Note that even misc/ahah.js passes an jQuery object:

  if (new_content.parents('html').length > 0) {
    Drupal.attachBehaviors(new_content);
  }


However, passing a string as context sounds a bit wrong.  Behaviors usually
expect a DOM element.

sun


Re: behaviors context

by Matt-192 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Thus my original claim: You are at the mercy of whatever calls
Drupal.attachBehaviors(). Unless you are absolutely positive you can
account for all use cases (and thus all callings of Drupal.attach()),
you're better off ignoring context.

Matt

On Fri, May 29, 2009 at 1:04 PM, Daniel F. Kudwien
<news@...> wrote:

>> The context is supposed to be the element to be
>> processed--not a jQuery object but the page element. See the
>> 5 - 6 upgrade notes:
>> http://drupal.org/node/114774#javascript-behaviors
>>
>> However, this fact may have been lost along the way.
>
> Note that even misc/ahah.js passes an jQuery object:
>
>  if (new_content.parents('html').length > 0) {
>    Drupal.attachBehaviors(new_content);
>  }
>
>
> However, passing a string as context sounds a bit wrong.  Behaviors usually
> expect a DOM element.
>
> sun
>
>



--
http://technosophos.com
http://querypath.org

Re: behaviors context

by Earl Miles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Matt wrote:
> Thus my original claim: You are at the mercy of whatever calls
> Drupal.attachBehaviors(). Unless you are absolutely positive you can
> account for all use cases (and thus all callings of Drupal.attach()),
> you're better off ignoring context.

I totally disagree. It's either a jquery object or something you can
easily make into a jquery object. Using $(context) should always work
and you can get a DOM element using $(context).get(0). Ignoring context
will lead to poor performance.

Re: behaviors context

by David Cohen-8 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

So just to clarify, will the context always refer to a single element?
In other words should my code be:

$elem = $(context).get(0);
do_my_behavior($elem);

or, more like:

$(context).each(function () {
  $elem = $(this).get(0);
  do_my_behavior($elem);
});


And in the first invocation, when context == document, will
$(document).get(0) return the document?

Thanks everyone for helping me understand.

-Dave


On Fri, 29 May 2009 11:44 -0700, "Earl Miles" <merlin@...> wrote:
> I totally disagree. It's either a jquery object or something you can
> easily make into a jquery object. Using $(context) should always work
> and you can get a DOM element using $(context).get(0). Ignoring context
> will lead to poor performance.

Re: behaviors context

by Matt-192 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

The other issue with using context is that it is unreliable for a
second reason: behaviors may actually misbehave when focusing on only
a small portion of the DOM. Example: context contains several rows of
a table, and you are adding odd/even classes to a table in a behavior.
If a behavior is fed just the modified rows, this may lead to
unexpected behavior (Reason: there may be other rows not fed into the
context, thus throwing off odd/even counts).

That's a basic example. In the JS book I wrote, I provided a more
elaborate examples of contexts causing problems. And there are still
others -- like cases where AHAH content has its own JS which may
modify your DOM further. But the basic principle is the same: Assuming
that the attachBehaviors call is passing you what you need is not a
good assumption. And since attachBehaviors can be called from anywhere
(not just in AHAH/AJAX callbacks) and for any reason, it's dangerous
to presume you know what you're getting.

re: performance... in most cases you are talking a negligible
difference. DOM optimization in mainstream browsers is good enough
that a search through an entire document is almost always "fast
enough". Shaving 10 msecs off of that time isn't going to make much of
a difference (assuming you could actually shave that much from the
search time).

There are two patterns in Drupal JS to "prevent" the same behavior
from processing the same data twice. One is the :not(processed-X)
pattern, which seems to be pretty reliable, and is pretty much
entirely in the hands of the author of the behavior (barring a
misanthrope who decides to remove your processed classes).

The other is the context, which requires that both the caller of
attachBehavior() and the behavior have the same set of assumptions
about what is being passed in and what needs operating on. This is not
always the case, and there are easy-to-reproduce gotchas (like zebra
striping, AHAH'd JavaScript that alters the DOM, and so on).

Matt

On Fri, May 29, 2009 at 1:44 PM, Earl Miles <merlin@...> wrote:

> Matt wrote:
>>
>> Thus my original claim: You are at the mercy of whatever calls
>> Drupal.attachBehaviors(). Unless you are absolutely positive you can
>> account for all use cases (and thus all callings of Drupal.attach()),
>> you're better off ignoring context.
>
> I totally disagree. It's either a jquery object or something you can easily
> make into a jquery object. Using $(context) should always work and you can
> get a DOM element using $(context).get(0). Ignoring context will lead to
> poor performance.
>



--
http://technosophos.com
http://querypath.org

Re: behaviors context

by Matt-192 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

No -- the context will refer to whatever is passed into
Drupal.attachBehaviors(). In Drupal core, that will be a DOM Element
or a jQuery object. I know of no guarantee that the jQuery will always
point to one DOM element (though I know of no place in Drupal core
where more than one is passed in).

For contributed modules... I suppose you are at the liberty of the
contrib developers.

Matt

On Fri, May 29, 2009 at 2:03 PM, David Cohen <drupal@...> wrote:

> So just to clarify, will the context always refer to a single element?
> In other words should my code be:
>
> $elem = $(context).get(0);
> do_my_behavior($elem);
>
> or, more like:
>
> $(context).each(function () {
>  $elem = $(this).get(0);
>  do_my_behavior($elem);
> });
>
>
> And in the first invocation, when context == document, will
> $(document).get(0) return the document?
>
> Thanks everyone for helping me understand.
>
> -Dave
>
>
> On Fri, 29 May 2009 11:44 -0700, "Earl Miles" <merlin@...> wrote:
>> I totally disagree. It's either a jquery object or something you can
>> easily make into a jquery object. Using $(context) should always work
>> and you can get a DOM element using $(context).get(0). Ignoring context
>> will lead to poor performance.
>



--
http://technosophos.com
http://querypath.org

Re: behaviors context

by Daniel F. Kudwien :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> re: performance... in most cases you are talking a negligible
> difference. DOM optimization in mainstream browsers is good
> enough that a search through an entire document is almost
> always "fast enough". Shaving 10 msecs off of that time isn't
> going to make much of a difference (assuming you could
> actually shave that much from the search time).

Now, you do the math:

10 ms * ~20+ behaviors = 200+ ms

With each module/behavior you add, the performance gets worse.

> There are two patterns in Drupal JS to "prevent" the same
> behavior from processing the same data twice. One is the
> :not(processed-X) pattern, which seems to be pretty reliable,
> and is pretty much entirely in the hands of the author of the
> behavior (barring a misanthrope who decides to remove your
> processed classes).

...which equally cannot solve your trivial zebra-striping table row example.
Once your table :is(.processed), your behavior can't (shouldn't) process it
again.

sun


Re: behaviors context

by Henrique Recidive :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/5/29 David Cohen <drupal@...>:

> So just to clarify, will the context always refer to a single element?
> In other words should my code be:
>
> $elem = $(context).get(0);
> do_my_behavior($elem);
>
> or, more like:
>
> $(context).each(function () {
>  $elem = $(this).get(0);
>  do_my_behavior($elem);
> });

It should be more like this:

Drupal.behaviors.myBehavior = function(context) {
  $(context).each(function () {
    // 'this' is a DOM Element.
    do_my_behavior(this);
  });
};

or you can include your selector and use context as it is supposed to be used:

Drupal.behaviors.myBehavior = function(context) {
  $('.my-behavior-class:not(.my-behavior-processed)',
context).each(function () {
    // 'this' is a DOM Element.
    do_my_behavior(this);
    $(this).addClass('my-behavior-processed');
  });
};


Henrique

>
>
> And in the first invocation, when context == document, will
> $(document).get(0) return the document?
>
> Thanks everyone for helping me understand.
>
> -Dave
>
>
> On Fri, 29 May 2009 11:44 -0700, "Earl Miles" <merlin@...> wrote:
>> I totally disagree. It's either a jquery object or something you can
>> easily make into a jquery object. Using $(context) should always work
>> and you can get a DOM element using $(context).get(0). Ignoring context
>> will lead to poor performance.
>

Re: behaviors context

by Matt-192 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Fri, May 29, 2009 at 2:59 PM, Daniel F. Kudwien
<news@...> wrote:

> Now, you do the math:
>
> 10 ms * ~20+ behaviors = 200+ ms

Actually, I was figuring 10ms for LOTS of behaviors. Not PER behavior.
A selector should clearly take well under 10ms. Any additional
overhead for a behavior is going to be the same regardless of whether
you use context or selectors.

>> There are two patterns in Drupal JS to "prevent" the same
>> behavior from processing the same data twice. One is the
>> :not(processed-X) pattern, which seems to be pretty reliable,
>> and is pretty much entirely in the hands of the author of the
>> behavior (barring a misanthrope who decides to remove your
>> processed classes).
>
> ...which equally cannot solve your trivial zebra-striping table row example.
> Once your table :is(.processed), your behavior can't (shouldn't) process it
> again.

Oh, come on... you wouldn't drop a zebra stripe processed class on the
table, would you? You'd drop it on the row. And if you found rows in a
table without striping, you'd re-process the entire table.

The problem occurs only when you assume the context is passing
sufficient information for your behavior. The context may in fact be
providing less information than you need, and you are at the liberty
of whatever called attachBehavior() in the first place (which may not
be something in core -- there's no way to tell). Consequently, it's
better to rely on the document itself, not just the context.

Matt

--
http://technosophos.com
http://querypath.org

Re: behaviors context

by Matthew Farina :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> The problem occurs only when you assume the context is passing
> sufficient information for your behavior. The context may in fact be
> providing less information than you need, and you are at the liberty
> of whatever called attachBehavior() in the first place (which may not
> be something in core -- there's no way to tell). Consequently, it's
> better to rely on the document itself, not just the context.

Sometimes you can't just rely on the document. For an example see the  
popups module. It attaches the behaviors to the body of the popup  
window. If a popup is built from an AHAH callback you wouldn't want to  
reapply the behaviors to the entire document for a popup that's just  
part of it. You'd want to apply it to just the new area.

If someone misuses attachBehaviors() should the behaviors be required  
to deal with that? They can't foresee everything and there are times  
where you want to attach behaviors to something other than the  
document. If they misuse attachBehaviors that would be a bug in their  
code.