Closed (fixed)
Project:
Drupal core
Version:
6.x-dev
Component:
javascript
Priority:
Normal
Category:
Feature request
Assigned:
Reporter:
Created:
18 Feb 2007 at 19:52 UTC
Updated:
19 Jul 2007 at 15:24 UTC
Jump to comment: Most recent file
Comments
Comment #1
nedjoHere's the beginning of a patch.
Still to do:
* testing (none done yet)
* tableheader.js (it's structured in a way that makes fixing it up a bit more complicated)
Pls test and report issues, better yet fix them. Anyone want to take on tableheader? Pls do.
Comment #2
Steven commentedDefinite +1 on this idea.
However, I think we need to go further in this. All autoAttach handlers should have an argument that specifies which content they should process (note that this does not negate the use of consistent 'foo-processed' classes). Also, all autoAttach handlers should be registered in a central array, and we should provide a function to invoke them.
Then, any piece of code that inserts new mark-up in the page can simply call this centralized handler. By only processing the new content (through the extra argument), we gain a speed bonus too. In drupal.js, we would set a single $(document).ready() event that invokes the centralized handler on the entire document.
That way, adding a new behaviour is as simple as e.g.
Drupal.autoAttach(function (content) { ... });.Comment #3
nedjoI've drafted code for this in the related issue here:
http://drupal.org/node/125153
It's basically the same approach as your idea Steven. We register an array of behaviours. They are added to the ready routine, but may also be attached at any time to any given content. Pls have a look. I need to finish that patch by changing the current .ready() calls calls to .registerEvent() calls.
Comment #4
nedjoHere's a full patch. I've rolled issue http://drupal.org/node/125153 into this one, to have a single patch that enables reapplying behaviors.
This patch makes the following changes, in addition to the two I outlined in first posting this issue:
1. New jQuery methods for attaching and calling behaviors.
We register behaviors like this:
where behaviorAttach is the name of an attach function. By default this behavior is added to the
$(document).readyarray, to be executed when the DOM is first ready to traverse. But it remains available after that in an array ofjQuery.behaviors.After appending new content (e.g., from newly loaded JSON), we can attach all registered behaviors like this:
or
This is basically the same as Steven's suggested approach.
2. Rename attach functions for consistency.
Some attach functions were named behaviornameAutoAttach, some behaviornameAttach. I've made the the latter the standard.
3. Move attach calls for system.js methods out of system.module.
Unlike all the other attach methods, the two in system.js were added through inline calls. For consistency, and because inline code makes it harder to generate a full list of available behaviors, I've moved these to system.js. Note: the callback to test clean URL support requires special treatment, as it is a behavior that will only need attaching once. For that reason I've left it out of the registered behaviors.
4. Added missing id for batch.js.
Comment #5
Steven commentedI'm not a big fan of extending the jQuery namespace with generic names like "behaviour". Unless this is a generic, separate jQuery plug-in, it deliberately misuses the namespaces we have.
Comment #6
nedjoYep, the aim is a generic, (potentially) separate plugin.
This answers a generic jQuery problem for which no standard solution exists, see this discussion:
http://www.mail-archive.com/discuss@jquery.com/msg17454.html
Once the patch is reviewed and accepted I plan to submit it as a jQuery plugin.
Would you prefer this to be a separate file? Does it need improvement as a plugin? Do you have alternate suggestions for how we should implement this?
Comment #7
recidive commentedGreat work!
Some suggestions:
I don't like the need of adding a class to every processed element, instead of this, we can pass the context (the content we have just added to the page, or its parent element) as an argument to the attach methods, so them can pass it back when calling
$()function. Currently the attach methods can usethissinceattachBehaviorschange the context to the element object when callingthis.apply(elem). But would not make sense to pass this explict as an argument to the attach functions, e.g. usingthis.call(elem)instead?I agree with Steven that this should extend the Drupal namespace, this way we can remove the
if (Drupal.jsEnabled)checks and add a single check to theattachBehaviorsfunction.Also, I wonder if we could remove the
$.registerBehavior()calls and build the behaviors array from php, in a similar way we do withDrupal.settings.Comment #8
recidive commentedI thought a bit more about this and come up with another approach. We can just instantiate attach methods extending Drupal.behaviors object, so instead of
it would be
then
attachBehaviorswould loop throughDrupal.behaviorsobject.Also I forgot to mention that you patch has a typo in batch.js, a space on the attach function
Drupal.batch Attach.Comment #9
recidive commentedI've changed the patch with my suggestions in #7 and #8.
I did some basic testing. Autocomplete, textarea, tableHeader, collapse, teaser. color and upload seems to be working fine.
Comment #10
nedjoHey, thanks Henrique. On consideration I agree, it's better to stick to the Drupal object. I like the simplicity of registering a behavior directly to the Drupal.behaviors object. Nice work.
Comment #11
dries commentedIf Steven has the time it takes, I'll let Steven carry this patch home. He started reviewing it already, and would be the best person for it anyway. :)
Comment #12
recidive commentedI did some changes in system.js to remove the 'processed' class stuff from
dateTimebehavior (tested and works). Also I'm wondering if we could make the clean url check reatachable too, think in a full AJAX administration interface.The patch is big mainly due to changes in color.js indentation.
Comment #13
Steven commentedAs far as I can tell, the color.js code isn't actually re-attachable in the last patch. It inserts the color picker without looking at existing marker classes.
However, I think the idea of making everything a behavior (even things like color.js, which only work on one particular element) makes sense in the long run. This gives everyone a simple 'no thought required' mechanism for making inserted ajax html 'just work', and forces consistent usage of 'processed' marker classes. This is a good thing, especially because your behaviour code becomes completely disconnected from specific events, like "onready".
Since you still need to call drupal_add_js() directly or indirectly for every behavior you actually use, there is no real performance hit either.
The only tricky situation would be where an ajax request loads HTML which uses behaviours that weren't part of the original page. In that case, the JS for them will not be loaded. However, I think such cases will be very rare and those problems would easily be solved by adding the right drupal_add_js() calls to the original page, using form_alter or something.
Comment #14
recidive commentedI've worked on another approach to make behaviors re-attachable, instead of looking for 'processed' marker classes, my approach pass the 'context' argument to the behaviors functions, then these functions can pass this back when calling
$().In other words, instead of writing things like:
We simply pass the 'context' to jQuery:
This approach is a lot cleaner than using the 'processed' marker classes IMO.
Comment #15
recidive commentedI forgot to add
= functionto the examples above, the correct examples are:In other words, instead of writing things like:
We simply pass the 'context' to jQuery:
The default context is 'document'. Here is an example (untested) of how re-attaching would work.
I think
addJsandcheckJswould be good additions to drupal.js, however this approach will not work with the js aggregation patch, so perhaps we can set and array, e.g. Drupal.loadedBehaviors, from php so we know what js files has been loaded with the current page?Comment #16
Steven commentedThe code above wouldn't work I think... you'd need to wait until the script has loaded before attaching behaviours.
However, I think marker classes should stay. For one, it lets you easily detect other behaviours' presence reliably. For another, it makes it easier to work around certain annoying browser bugs that require repeated attachment (e.g. with the collapsed fieldsets).
Comment #17
ChrisKennedy commented+1 to the general idea
Comment #18
recidive commentedI just discovered jQuery ships with a method for including js files via
xmlHttpRequest. The method is$.getScript(url, callback).Do you think it's worth adding some functions to drupal.js for automating the process of loading .js and .css files, as well as merging 'settings' loaded via Ajax to
Drupal.settingsobject?I don't see how the 'processed' class and the 'context' argument approaches can coexist. Sounds somewhat redundant for me. Can you elaborate on the bugs you mentioned?
I'll re-roll the patch as soon as possible. Assigning to myself.
Comments are welcome :)
Comment #19
nedjoIt's tempting to deal with loading scripts as well, but I suspect that's better left out of this patch at this point. Only three days to code freeze. Let's try to deal with the minimum. Dynamic loading would then be possible in contrib, if we can't get it in now.
A complication for dynamic loading is preprocessing of css and (possibly soon) js. in that we then don't have distinct files.
Comment #20
recidive commentedOk, I've re-rolled the patch with the following changes:
Did some basic testing on every 'behavior'.
Comment #21
recidive commentedSyncing with HEAD.
Comment #22
nedjoThanks Henrique, this is looking good.
Changing title to help clarify the main benefit of the patch.
I think it's best to keep the 'processed' classes. Yes, if we use the context argument we can limit our action to a given new element, but there may be use cases where we wish to reattach behaviours to a whole page, or, say, a parent element we've just appended content to.
Comment #23
ChrisKennedy commentedNeeds a re-roll after the js aggregator patch.
Comment #24
recidive commentedHere is the re-rolled patch.
Comment #25
recidive commentedAs per Steven's and Nedjo's request, I've added back the 'processed' classes.
Comment #26
Steven commentedThe color.module behaviour is not really re-attachable since it can only be used once (it uses id's). Better would be just to check if the known target element has been processed at the start, and then do nothing (
return;) if so.Comment #27
wim leersSubscribing.
Comment #28
nedjo@Steven:
Good point. Here's a version that returns if there is already a processed element on the page.
openid.js was added since the previous patch. Since it also uses an ID to attach, I've implemented the same general approach there (only reattach if a processed element doesn't exist).
This probably needs one more quick test - particularly the openid changes - and it's ready.
I've applied the Drupal.behaviors approach to the Javascript Tools package (HEAD version). The updating was quick and easy, and it's proving its worth. Reattaching behaviors becomes a breeze. I've also implemented in the dynamicload module dynamic loading of js and css files and dynamic updating of the Drupal.settings object. We could consider bringing those pieces into core at some point in a later patch. The main problem is that we need to send the client structured JSON data on the JS and CSS. I've done that through a helper module, pagearray, but for core we'd need a better solution.
Comment #29
nedjoI've given the patch a thorough testing and workover and corrected several remaining issues. Tested with and without JS preprocessing. All behaviors work as expected. IMO this is now RTBC.
Changes made since the last patch:
Drupal.behaviors.cleanURLsSettingsCheckto ensure we're not installing. Also added a check for already attached behavior, as this attaches by ID.Drupal.behaviors.batchto attach only once (ID).Drupal.attachBehaviors().$(), etc.Comment #30
dries commentedWhile the last couple of weeks, I spent a fair amount of time writing/practicing jQuery code, and also read up on AHAH/behaviours.
In general, this patch looks good, but I'm thinking it would be useful to formulate some guidelines here. For example, the way you postfix IDs with '-processed' to indicate that they are processed, the behaviors-namespace and the way Drupal picks up these behaviours. That's probably something we'll want to document.
Some of this should probably go into a handbook page, but I'd also like to see us improve the inline documentation. Specifically, I suggest we describe the overall design in the PHPdoc comments of
Drupal.attachBehaviors.I spent 30 minutes looking at the code trying to figure out how Drupal picks up behaviors and to discover some of the patterns in how behaviors are written. Some inline documentation would have saved me 20 minutes so let's make sure that other people can pick this up a little easier.
Thus, expand the documentation of attachBehaviors and explain briefly (i) what we mean by 'behaviors', (ii) why we are using behaviors, (iii) how Drupal picks up behaviors so people can figure out how to tab into the system and (iii) what to be careful about. It can be brief and to the point but at least, it gives people a starting point. I can probably be done in 5 sentences or so. We can then use the Drupal handbook for more verbose explanation with examples.
I'd move this function up in the .js file so it's easy to spot.
Other than that, the patch looks RTBC. Great work, nedjo.
Comment #31
dries commentedI'd like to commit this patch too: http://drupal.org/node/154398. Might be useful to see if there any conflicts.
Comment #32
nedjoRefreshed patch with documentation following Dries' suggestions and the
Drupal.attachBehaviorsfunction moved near the top of drupal.js.Here's the new documentation for
Drupal.attachBehaviors:Is this clear, succinct, and explanatory?
Comment #33
Frando commentedThe patch and the new JSDOC ;) looks great! It's breaking the JS teaser splitter, though. I'm looking into it.
Comment #34
Frando commentedHm. Or maybe it doesn't. Weird, it seems to work now after reapplying the patch.
Comment #35
Frando commentedOk, did one round of reviewing this.
I tested pretty much all of Drupal's JavaScript behaviours (fieldsets, farbtastic, autocomplete, file attachments, openID login, teaser splitter, ..) and did not found one single problem. The new comment for Drupal.attachBehaviors is really nice and extensive now. This is ready to go. Great work!
Comment #36
dries commentedCommitted to CVS HEAD. Thanks nedjo.
Comment #37
nedjoAnd thanks recidive, who rewrote and substantially improved the initial draft.
I'm working on documentation, in a handbook page in the section on Javascript and the Drupal 5 - 6 module updating page. I'll post links here when that's done.
Comment #38
nedjoAdded some basic updating instructions, http://drupal.org/node/114774#javascript-behaviors. They could use filling out, e.g., to include instructions for how to handle behaviors that attach by ID.
Comment #39
(not verified) commented