How to add Javascript in Drupal 7 with jQuery

I thought this would be an easy one. Not so. What works in Drupal 6 doesn't necessarrily work in Drupal 7.

Let's take a simple example. How about a script that allows you to show/hide one or more paragraphs. I did a quick Google search and found numerous examples. Here's the one I picked & slightly modified for the sake of clarity.

<script type="text/javascript">
    $(document).ready(function(){
      $(".toggler").click(function(){
        $(this).next().slideToggle("slow");
        return false;
      }).next().hide();
    });
</script>
    
<p class="toggler" style="cursor:pointer;">Show/Hide the following paragraph(s).</p>
<div>
    This paragraphe is initially hidden. Clicking on the link above will reveal (Show) all the content inside the DIV that immediately follows a "toggler" class paragraph.
</div>

In Drupal 6, simply paste this code in the Body field of a Page or Article node and it works. Well, as usual, there are a few requirements. For the most part, this should do it.

  • Go to admin/settings/filters/2 (Full html settings) and uncheck the HTML filter.
  • Set the Input format of the Body field to Full html.
  • Disable rich-text
  • Paste the above code, save and enjoy a moment of true happiness.

The Drupal 7 way

I tried this exact same method in Drupal 7 but by default the <script></script> tags and its content are removed (deleted) by the system for security reasons. There are probably ways to bypass this but it's bad practice.

So we can't embed this script in a Drupal 7 Article node. In the present case, the next best way - a proper way - is to embed the script directly into the Article template of the theme. In doing so, the script will be available for all nodes created with the Article content type. Now, that's even better. So how do we do this?

Click here to Show or Hide the next paragraph!

I'm using the Antonelli theme, a fluid width subtheme for Bartik. In the Antonelli theme folder (sites/all/themes/antonelli), there is not a simgle template. So I had to look to Antonelli's parent theme for help. In the Bartik folder (themes/bartik), under templates, there is no template for nodes created with the Article content type. But I found node.tpl.php, a generic template for all nodes. This is the template I'll use to create a custom node template for the Article content type. Here's how to do it.
  • Duplicate node.tpl.php and rename it node--article.tpl.php.
  • Open it up with your favorite text editor.
  • Paste just the script part of the code before the first line of html.
  • The script must then be wrapped inside a jQuery closure function. Otherwise it just won't work. See the References block for more information.
<script type="text/javascript">
    (function($) {
        // Javascript code
    }) (jQuery);
</script>
  • Save it in your theme's folder. For me, that would be sites/all/themes/antonelli.
  • To load the template you just created Resave your theme or Flush all caches.

In the end, here's what the node--article.tpl.php should roughly look like.

 ...
 * @see template_preprocess()
 * @see template_preprocess_node()
 * @see template_process()
 */
?>

<script type="text/javascript">
    (function($) {
        $(document).ready(function(){
          $(".toggler").click(function(){
            $(this).next().slideToggle("slow");
            return false;
          }).next().hide();
        });
    })(jQuery);
</script>


<div id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
...

Ready for some magic?

  • Create a new article.
  • Make sure the Input format of the Body field is set to Full html.
  • Disable rich-text.
  • Paste the following code.
<style type="text/css">
  p.toggler:hover {
    color:#0071B3;
  }
</style>

<p class="toggler" style="cursor:pointer;">Show/Hide the following paragraph(s).</p>
<div>
    This paragraphe is initially hidden. Clicking on the link above will reveal (Show) all the content inside the DIV that immediately follows a "toggler" class paragraph.
</div>

Save and then Show/Hide to your heart's content!

Comments

Nice catch, but you should really be using Drupal Behaviors instead of directly binding events!

Hi Alex. It would be great if you could expand on how you would use Behaviors in the current example.

Using Drupal behaviors and jQuery once, the code would be

    (function($) {
       Drupal.behaviors.customToggler = {
         attach: function(context, settings){
            $(".toggler", context).once('custom-toggler').click(function(){
              $(this).next().slideToggle("slow");
              return false;
            }).next().hide();
          };
        };
    })(jQuery);

This allows the script to work for elements added after the page loads.

I also suggest to remove the address tag ( <a href=""> ... </a> ) leaving the clickable text inside the "p" and adding a line of CSS with the "cursor:pointer" property.

Done. Thanks for the suggestion.

Check out: http://drupal.org/node/756722 for more info on Drupal Behaviors. It's basically a wrapper for jQuery.ready() that uses jQuery.once(). It's good practice to use it, and it also gives you context and settings variables. Context is either the document element or, when you are in forms for example and the js is added using #attached, its the form object for instance. It's useful to optimize selectors, so you can do $('#foo', context) and you can search against the context. :)

It would look something like this:

    (function($) {
        Drupal.behaviors.my_node_toggle = {
          attach: function(context, settings) {
            $(".toggler", context).click(function(){
              $(this).next().slideToggle("slow");
              return false;
            }).next().hide();
          });
          }
        };
    })(jQuery);

That said, I believe your javascript could be put in an external file and added using a preprocess funciton to keep the template free of non-markup! :) Sorry, don't mean to be annoying or a stickler! :)

likewise the css belongs in a file instead of style tags, again being anal
also you might want to use jquery once or add a 'processed' class/filter to your jquery to prevent the listener being attached twice (eg on ajax events completing) - eg

$('.toggler:not(.processed)', context).addClass('processed')...

Drupal.attachBehaviors gets called when ajax events complete too.

How would you only include the behaviour for certain content types? Presumably this would mean examining the node type and only including the link to the external file accordingly. I'm still new to function writing, so an example would be nice to see.

Hi everybody! Iam a drupal rookie and need a solution to make javascript comandos work within the webform of drupal 7. In this case I need to know: How can I find the solution to automatically convert the very first letters of every single word which the user inserts in the field "name and surname" of the contact form. The idea to use this conversion is because I want to have this in the autoreply so that the salutation is "Dear Mr. John Smith" instead of "Dear Mr. john smith". I know it's a little something and for many not that important, however it would be very interessting to know how to do this. I have found a sample code, maybe somebody could change it and tell me what exactly I have to do I'd appreciate any help. Thanx in advance

Hi everybody! Iam a drupal rookie and need a solution to make javascript comandos work within the webform of drupal 7. In this case I need to know: How can I find the solution to automatically convert the very first letters of every single word which the user inserts in the field "name and surname" of the contact form. The idea to use this conversion is because I want to have this in the autoreply so that the salutation is "Dear Mr. John Smith" instead of "Dear Mr. john smith". I know it's a little something and for many not that important, however it would be very interessting to know how to do this. Maybe somebody could tell me what exactly I have to do I'd appreciate any help. Thanx in advance

@Janin - Just css capitalize first letter with jquery. done - no need to post such a question twice...

Add new comment

Plain text

  • Lines and paragraphs break automatically.
  • Allowed HTML tags: <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
Type the characters you see in this picture. (verify using audio)
Type the characters you see in the picture above; if you can't read them, submit the form and a new image will be generated. Not case sensitive.