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
  • Insert the <script></script> tags 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
  • Finally, there are advantages to use Drupal behaviors rather than document.ready()
<script type="text/javascript">
    (function($) {
      Drupal.behaviors.customToggler = {
        attach: function(context, settings) {
          // 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($) {
       Drupal.behaviors.customToggler = {
         attach: function(context, settings){
            $(".toggler", context).once('custom-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 (CSS is optional)
<style type="text/css">
  p.toggler:hover {
    color:#008000;
  }
</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!

Submitted by Alex Weber (not verified) on Mon, 02/20/2012 - 00:32

Permalink

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

Submitted by admin (not verified) on Mon, 02/20/2012 - 07:34

In reply to by Alex Weber (not verified)

Permalink

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

Submitted by mongolito404 (not verified) on Mon, 02/20/2012 - 08:17

In reply to by admin (not verified)

Permalink

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.

Submitted by admin (not verified) on Fri, 10/18/2013 - 10:53

In reply to by mongolito404 (not verified)

Permalink

Thanks for the code mongolito404. One small fix. The semicolon in third line from bottom should be deleted otherwise the script will not work. Cheers.

Submitted by Anonymous (not verified) on Mon, 02/20/2012 - 04:28

Permalink

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

Submitted by Alex Weber (not verified) on Mon, 02/20/2012 - 09:35

Permalink

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. :)

Submitted by Alex Weber (not verified) on Mon, 02/20/2012 - 09:38

Permalink

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.

Submitted by Janin (not verified) on Fri, 05/18/2012 - 13:39

Permalink

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

Submitted by FakeBit (not verified) on Thu, 09/20/2012 - 04:04

Permalink

@Janin - Just css capitalize first letter with jquery. done -

Submitted by Josue (not verified) on Thu, 10/17/2013 - 14:13

Permalink

Hello,

Your code is wonderful. It works out inside article content but when I tried to do the same thing with basic page, it doesn't work. Do you have any idea where is my problem?

I use bartik theme

Hi Josue, Make sure you have a custom template for the Page content type (make a duplicate of bartik/templates/node.tpl.php and change its name to node--page.tpl.php), paste in the javascript code and save. Don't forget to flush all caches.

Submitted by Mary (not verified) on Thu, 10/17/2013 - 21:38

Permalink

I am designing a website, so that each user is able to put comment on any paragraph. So in my website instead of displaying :" Click here to Show or Hide the next paragraph! ", it will give the option of commenting. But my problem is how to recognize each paragraph in the program. ( I know each paragraph is surrounded in <p> and </p>) Is there any modulo for that?

To make this script available on all pages, copy-paste the code into a toggler.js file and put it into your theme's folder (e.g. sites/​all/​themes/​mytheme/​js/​toggler.js) and load it from your theme's .info file with scripts[] = js/toggler.js. I suppose you could also adapt this script to make it work for comments specific classes.

Submitted by Josue (not verified) on Mon, 10/21/2013 - 10:58

Permalink

Hi admin,

Thank you very much for your answer. I tested your solution and it works fine. I'm so sorry but I have one more question. I explain you my problem.

I created one new content type named cleanboy product to show products from commerce module. I installed too Display suite module to customize the cleanboy product display. Finally I copied et pasted your code into sites/​all/​modules/​ds/​layouts/​ds_2col_stacked_fluid/​ds-2col-stacked-fluid.tpl.php file and it works without problem.

But my new problem appears when I create one view to show all products. I see the link to collapse the paragrahp. When I click on "Show/Hide the following paragraph(s)" it collapses but it doesn't stay. Do you have any idea where is the problem?

Thanks

Submitted by admin (not verified) on Tue, 10/29/2013 - 08:16

Permalink

Are you using the 'Drupal behaviors and jQuery once' approach as suggested by mongolito404 and Alex Weber ? If not, it is possible that the problem stems from the fact that the script is loaded for each row of your view. That being said, Views is beyond the scope of the current article.

Submitted by drupal.org (not verified) on Thu, 11/21/2013 - 13:35

Permalink

Corbin, Have you read the comments above your own where it references drupal.behaviors?
I'd also read through Managing JavaScript in Drupal 7

Submitted by Aleksandr (not verified) on Fri, 08/22/2014 - 04:14

Permalink

I user PHP filter to embed code in body without pasting it in template

Submitted by Gauri Shankar (not verified) on Mon, 08/25/2014 - 04:55

Permalink

Hi.

Writing php code in body part it may slow the site performance. So keep in mind at the time of writing code in body part.