behaviors.module: docking JavaScript behaviors into Drupal

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
kkaefer's picture

Now that we have jQuery in Core, there are new JavaScript enhancements for Drupal popping up everywhere. Most of these enhancements are pretty small. To add them to a page, it usually requires a module. But those modules don't do much more than just adding the JavaScript file with drupal_add_js - the whole module system with all of its callbacks and so on is just overkill for that problem.

On #drupal we have discussed the need for a module that gathers all those JavaScript enhancements in just one module. In the rest of the proposal, I will use the word "behavior" for a JavaScript enhancement file.

Features

There will be two ways to add behaviors to a Drupal page:

  1. Listing page
    • A page, similar to the modules page, that lists all available behaviors
    • Allows the administrator to turn on and off behaviors (like blocks, modules)
    • Configure behaviors so that they are just added on some pages (like on the blocks configuration page)
  2. hook_behaviors for modules
    • Allows modules to provide a list of pre-defined behaviors that will then be visible on the listing page
    • Examples are: upload_behaviors for adding the upload.js, ...
  3. /behaviors/
    • A new directory on the modules and themes level is added: behaviors (in /, /sites/all, /sites/*)
    • Contains *.behavior files that are JavaScript files with a ini-style configuration at the top to provide descriptions

Please post your opinion about that module proposal, make suggestions and enhancements to it.

Comments

perhaps

moshe weitzman's picture

i think i like the idea. there are a few more nice to have items

  • a new directory in Contrib repository called behaviors which is at same level as modules and themes
  • project.module should know about behaviors and list them
  • system.module should know about behaviors and version them? do they ever need DB storage or a settings page? maybe we skip this and recommend people use modules at this point.

Yes, good points. If we got

kkaefer's picture

Yes, good points. If we got that far and create a new root level contrib directory, we should probably add the module to Drupal core. Because others could come and ask for their "own" directory.

Behaviors don't need any DB storage. If they need that, a module that handles all that stuff should be created (probably using the hook_behaviors).

The simple behaviors are for example for things like auto-focusing textfields, making an upload field upload inline, attaching files like collapse.js and so on. That means: JavaScript files that do a single limited activity on that page: unobtrusive JavaScript behaviors.

Good idea!

nedjo's picture

Yes, I think collecting these behaviors in one place is the way to go. A different module for each is certainly not needed.

However, some behaviours will need access to Drupal hooks. For example, a behaviour might need to do form-alters. Here are some examples from the Javascript Tools module.

  1. collapsiblock (makes designated blocks collapsible). form_alters 'block_admin_configure' form to allow selection of whether to make a block collapsible.

  2. dynamicload (allows designated blocks to load their content dynamically into a given target area): also form_alters 'block_admin_configure'.

  3. ajaxsubmit (allows designated forms to submit via ajax): uses form_alter and registers menu callbacks.

We could handle this though through some custom invoking. I've done something similar in another module. Assume e.g. that each behavior is in its own directory. Example: helloworld behavior (pops up 'hello world' when user clicks a link).

In the directory we have files like the following:

helloworld.behavior [php file]
helloworld.js
helloworld.css
hello.png

In the behaviors module, e.g., behaviors.module, we include code that system scans for .behavior files. We could consider registering them to the system table as type 'behavior', or else just scanning each time we need them. In either case, we include invoke functions that look for implementations of hooks in the behaviors. Here's some sample code:

<?php
/**
* Invoke a hook in all available behaviors that implement it.
*
* @param $hook
*   The name of the hook to invoke.
* @param ...
*   Arguments to pass to the hook.
* @return
*   An array of return values of the hook implementations. If behaviors return
*   arrays from their implementations, those are merged into one array.
*/
function behaviors_invoke_all() {
 
$args = func_get_args();
 
$hook = array_shift($args);
 
$return = array();
 
// Load all behaviors.
 
behaviors_load();
 
$behaviors = behaviors_available();
  foreach (
array_keys($behaviors) as $behavior) {
   
$function = 'behaviors_'. $behavior .'_'. $hook;
    if (
function_exists($function)) {
     
$result = call_user_func_array($function, $args);
      if (isset(
$result) && is_array($result)) {
       
$return = array_merge($return, $result);
      }
      else if (isset(
$result)) {
       
$return[] = $result;
      }
    }
  }
  return
$return;
}
?>

This is uses in cases where no argument is passed by reference. We would need a separate version for each place where args are passed by reference. E.g.:

<?php
function behaviors_invoke_all_first(&$arg1) {
  ...
}
?>

<?php
function behaviors_invoke_all_second($arg1, &$arg2) {
  ...
}
?>

Then for each supported hook we put a wrapper function that invokes the hook in all behaviors:

<?php
/**
* Implementation of hook_menu().
*
* Load menu items of available behaviors.
*/
function behaviors_menu($may_cache) {
  return
behaviors_invoke_all('menu', $may_cache);
}
?>

Then our sample behavior could respond to hooks in its .behavior function:

<?php
function behaviors_helloworld_menu($may_cache) {
  ...
}
?>

Rather than jumping into a new directory in contributions, it might be better to start off with a subdirectory in a module, e.g., contributions/modules/behaviors/behaviors. I guess it would need some special access handling though to allow individual contributors to edit their own behaviors without making the entire module's directory open.

Auto-behavior

John Resig's picture

I dig this idea - here's an idea for something that I can do on my (jQuery's) end:

We're in the process of setting up an official plugin repository, where plugins will be classified by type. I can provide a classification for 'behavior' style plugins (I was planning on doing this anyway, but at least the naming can be similar).

Now here's the cool part: I can also write a quick script that auto-makes a Drupal 'behavior' module for any behavior-style plugin in jQuery. Now a Drupal user could find any jQuery behavior plugin and quickly import it straight into their site.

Most likely, without too much work, we could auto-import jQuery behavior plugins into the master Drupal Behavior repository, keeping everything up-to-date.

All of this would require very little work on our end (since we're moving ahead with most of this already), just let me know if this is a good direction to move into.

thanks - nice

moshe weitzman's picture

this cooperation sounds terrific. i'm a little unsure though when you say that you could "auto-create a drupal behavior module". i think you are using the word 'module' loosely there, since the whole point of this thread is to avoid creating separate modules for each of these small features. maybe someone else can chime in on that.

Now that I think of it, perhaps we can leverage our new .info files for this. We can just let behaviors be directories under modules directory. Then we require a new line in .info files called 'Type'. If not present, we assume type=module. The other supported type is 'behavior'. That gives us all we need to do enable/disable like modules and so on. Food for thought.

That's an interesting

kkaefer's picture

That's an interesting approach but we have two problems here:

The main reason to avoid having too many modules is that invoke_all tries to find all hooks for a module, even if it just implements hook_menu for adding a js file. The intention that lead me to writing initial post was that there are behaviors that do not require any Drupal functionality and just live in their JS file. A textfield autofocus, the update.js, collapse.js, autocomplete.js etc. would be an example.

The second problem is that we are kind of abusing the naming convention here: we would have modules of type module and modules of type behavior. That's a bit weird imo.

naming clarification

moshe weitzman's picture

well, i would call them packages of type module and pakages of type behavior. it is true that if we didn't change anything, both would live under the modules directory which is a bit odd.

Kicking in

Steven's picture

How do you plan on finding out which behaviours are needed on a page? A block-like path based matching is good for end-users, but is useless to a behaviour coder.

Should "behaviors" be in themes?

Anonymous's picture

I was thinking about this issue and thought maybe behaviors are really part of a theme. I could be wrong. I might have a Jquery plugin that only works in X browser and want to apply it only to X theme. On that note I may not want the jquery plugin applied to any other themes I have installed.

Just a couple of thoughts.

My cents...

recidive's picture

I think we can can start porting some functionality already done in the jstools module usign its directory structure and perhaps we can add a 'contrib' directory where we can put the contributed behaviors. Here are my thoughts:

  1. Behaviors as modules or not?

    Making behaviors as modules we have the ability to have database tables for behaviors and we don't have to work on a admin/behaviors page that is no more than a clone of the admin/modules page. We can just create a package "Behaviors" using .info files. See this screenshot.

  2. Persisting behavior states

    We can have a persistency mechanism in behaviors module that stores behavior states by user into a table (uid, behavior, data). We can use this intead of setting cookies. E.g. for collapsible blocks, when the user choose to collapse a block, we serialize the block state and send to a callback via ajax that stores that into the persistency table.

  3. The Behaviors namespace

    We can create a javascript namespace to hold our behaviors objects like the "Drupal" namespace, but we can also use jQuery's "$." namespace for that.

  4. jQuery plugins

    Currently the Drupal CVS rules doesn't accept mirroring of files, but it would be great if we can provide 3rd party jQuery plugins such as Interface without the need to instruct users to download these from another place.

The behaviors project is already created. We need to work out a list of developers that will be allowed to commit in that project and contact the project owner to set these permissions to us.

I'm planning to commit a draft module into my sandbox soon.

Javascript

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds:

Hot content this week