npm Blog (Archive)

The npm blog has been discontinued.

Updates from the npm team are now published on the GitHub Blog and the GitHub Changelog.

Using jQuery plugins with npm

Last week we covered how you can publish jQuery plugins to npm, the quick and dirty way. By quick and dirty, we meant that you could simply publish your module as is to npm—without any modifications—and people could use it. But some plugins go the extra mile and add a little bit of code to make it easier to use with npm-based tools.

In this article, we’ll show you how you can use jQuery plugins that are published the quick and dirty way… that is to say, plugins that don’t use the main field of package.json or that set a global variable no matter whether they’re being used directly in the browser or being bundled with a tool like Browserify.

In the next article, we’ll show you how to update your plugin to make use of npm-specific features, which makes life easier for the developers using your module (and for you when you use your own plugins on projects). We’ll also show you how to make it work with the module system that Node uses, CommonJS, and make the best use of modularity to make your plugin more maintainable and easier to build upon.

What kind of plugin

The kind of plugin that we’re working with does not export a Node-compatible module (using the CommonJS module format), and doesn’t expect to work with with such modules. Instead, it simply expects that a jQuery object will have been set up (in the global scope) for the plugin to work with.

However, even if a plugin does know how to work with Node-compatible CommonJS modules, you can still use the methods we describe here, so you don’t need to worry about figuring out the difference just yet. We’ll cover the difference in the next article.

We’ll be developing a simple page which uses the tipso tooltip plugin. You can find the final code for this demo on GitHub.

Multiple approaches

We’ll be discussing multiple approaches. There are pros and cons to all of the different methods of handling front-end assets with npm. We want to help you pick the strategy that’s best for your team’s development workflow. We’ll be starting with the simplest workflow and building up from there to a cleaner, more maintainable workflow in the next article. We’ll discuss the tradeoffs you have to consider throughout this article and future articles.

The two approaches we’ll explore in this article are:

  1. Including the script files directly
  2. Bundling the files with Browserify

Let us know in the comments or on Twitter if you have different approaches.

Setting up your project

For both of these methods, we’ll need to set up a project.

  1. Create a directory
    mkdir demo
    cd demo
    
  2. Add a package.json file with the following contents
    {
      "name": "demo",
      "version": "1.0.0"
    }
    
  3. Install the tipso package
    npm install --save tipso
    
  4. The --save flag adds it to your package.json file as a dependency, which is helpful when you’re working on real projects because it makes it easier to update your dependencies and easier for others to download the dependencies if you share the project.

Method 1: Including the files directly

This approach will look very familiar, and it is the simplest to get working since it doesn’t require any extra tooling. You will just be adding a script tag to your HTML which uses the full path of the .js file as the src.

  1. In the root of your directory, create an index.html file.
  2. Add the following line to the body.
    <span class="title-tipso tipso_style" title="This is a loaded TIPSO!">Roll over to see the tip</span>
  3. Add jQuery from a CDN. Any of the CDNs will do.
    <script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
  4. Make the dependency tree as flat as it can be
    npm dedupe
    Because we’re declaring all of our dependencies in package.json, we don’t really need to do this, but it’s a good practice.
  5. Add the tipso package’s JavaScript
    <script src="node_modules/tipso/src/tipso.js"></script>
  6. Add a file named script.js with your script, which uses tipso to add a tooltip.
    jQuery(function(){
      jQuery('.title-tipso').tipso();
    });
  7. Add tipso’s CSS
    <link rel="stylesheet" href="node_modules/tipso/src/tipso.css" />
  8. Open index.html in your browser and roll over the text. You should see the tooltip pop up in a bubble.

You’ll end up with a document that looks like this:

<!doctype html>
<html>
<head>
  <title>npm and jQuery demo</title>
  <link rel="stylesheet" href="node_modules/tipso/src/tipso.css" />
</head>
<body>
  <span class="title-tipso tipso_style" title="This is a loaded TIPSO!">Roll over to see the tip</span>

  <script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
  <script src="node_modules/tipso/src/tipso.js"></script>
  <script src="script.js"></script>
</body>
</html>

Drawbacks

There are some drawbacks to this method.
  1. This approach isn’t very maintainable. The dependency that tipso has on jQuery isn’t defined anywhere except in the developer’s head. If a new developer comes on, or if you come back to this code 6 months later, it won’t be obvious which scripts need to be added when you need to repurpose a feature on another part of the site.
  2. This approach also isn’t as efficient as it could be in terms of the number of requests you’re making for assets. There are four requests being made here for files—your script, the tipso JS, the tipso CSS, and jQuery (though that’s coming from a CDN, so is likely to be cached already). The inefficiency of handling each file separately isn’t as obvious for a demonstration like this, where there’s only a few files, but it will become noticable on a larger project.
  3. Your node_modules directory structure could change. Since the paths are hardcoded in index.html, your app will break if you run npm update and directories get moved around. It’s unlikely to happen in a simple app like this, but it’s something to watch out for on more complicated projects. There are workarounds you can use for this which we won’t cover here.

We won’t be able to fix issue 3 until we make improvements to the plugin itself in the next article, but we can fix issues 1 and 2 by using a tool called Browserify, which brings some other benefits as well.

Method 2: Bundling files with Browserify

Browserify is a tool that makes it easier to use back-end and front-end packages together, and then bundle all of that JavaScript up into a single file which you can include in your HTML. For example, with Browserify you can use the ngraph.generators and ngraph.vivasvg packages to create an exploding dot animation.

Besides making it easier for your front-end code to use the 127,000+ packages on npm, Browserify also makes it easier for your team to develop in a modular way, which improves the structure of your code and makes it easier to maintain. You’ll see this in action in the next article.

Many people don’t realize this, but even if you’re working with a jQuery plugin that isn’t CommonJS compatible, you can still use Browserify.

Setting up Browserify

Browserify is a command line tool that you use when building your project. It isn’t loaded when you’re running your app. This means it should be installed globally, not as a dependency of the project.

npm install -g browserify

If you get an error that says EACCES, check out our docs on fixing permissions, or just run the command with sudo.

Bundling up JavaScript

First, we’re going to bundle up the JavaScript that we’re using.

  1. Prepare index.html for the change by removing the existing script and link tags, and adding a single script tag which references bundle.js:

    
    <!doctype html>
    <html>
    <head>
      <title>npm and jQuery demo
    </head>
    <body>
      <span class="title-tipso tipso_style" title="This is a loaded TIPSO!">Roll over to see the tip
    
      <script src="./bundle.js">
    </body>
    </html>
    

    The bundle.js file is what Browserify will produce for us. It will include all of our JavaScript packages, along with a way of ensuring that dependencies are properly passed in to the modules which depend on them.

  2. Add jQuery as a dependency. This will make it easy to require it in your other .js files.
    npm install --save jquery

    Note: You could continue to use the jQuery script tag if you wanted to, instead. We are declaring it as a dependency (and requiring it in the next step) for consistency in dependency handling, but you may want to maintian the performance benefits of a CDN-ed jQuery.

  3. Create an entry.js file.
    global.jQuery = require('jquery');
    require('tipso');
    
    jQuery(function(){
      jQuery('.title-tipso').tipso();
    });

    This will be the entry point where Browserify will start. From there, it will look for dependencies and follow the dependency tree to see what code it needs to include in bundle.js.

    It’s the same as the script we had in index.html before. But this time, instead of including jQuery and tipso as separate script tags, we’re using Node’s require function to let Browserify know that we depend on them.

    When it’s loaded in the browser, tipso attaches itself to the global jQuery object. However, in Node.js the variables that we declare with var are local to the module, rather than being global. For tipso to attach itself, we need to expose jQuery as a global, so we’ll attach it to the global object which Browserify provides.

  4. Tell browserify where it can find tipso. To do this, add a browser key inside of your package.json
    "browser": {
      "tipso": "./node_modules/tipso/src/tipso.js"
    }
    See it in context. If the main field in tipso’s package.json file pointed to the tipso.js file, we wouldn’t need to do this. We’ll cover this in the next article.
  5. Run browserify to create bundle.js
    browserify entry.js --debug > bundle.js
    

    We use the --debug flag to add source maps to the bundled file. This will make it easier to debug in the browser’s console.

  6. Load index.html and roll over the text. You will see the text pop up, but it won’t have the nice bubble contour. This is because we haven’t added the CSS back in yet.

Adding the CSS

The best practice for handling front-end assets, such as CSS files, is still developing. The community has come up with multiple solutions, but none is a clear winner. We’re talking through the issues with projects in different front-end ecosystems to figure out the best strategies for everyone, and will be developing better support, but for now there’s no well established way to do it.

The easiest way that we’ve found is to use parcelify, but you might also find different approaches that work better for you.

  1. Install parcelify globally
    npm install -g parcelify
  2. Tell parcelify where it can find the CSS that you need. To do this, add a style key inside of your package.json
    "style": [
      "./node_modules/tipso/src/tipso.css"
    ]
    See it in context.

    This is another step we could eliminate by making a change to the tipso package, and we’ll see how in the next article.

  3. Run parcelify to create bundle.css
    parcelify entry.js -c bundle.css
    
  4. Add a link element for bundle.css to index.html
    <link rel="stylesheet" href="bundle.css" />
  5. Reload index.html and roll over the text. The bubble CSS has been restored.

Using a CDN for jQuery (optional)

In the first approach, we used a CDN to source jQuery. You might have noticed that the second approach didn’t do this. Instead, it bundles jQuery up into the bundle.js file. This means that bundle.js is a heavier file than it needs to be.

But this doesn’t have to be the case. You can use this method and still use a CDN for jQuery using a tool called browserify-shim. We won’t cover that usage here, but you can find out more about it on browserify-shim’s README.

Wrapping it up

In this article, you learned how to use a jQuery plugin that has been published to npm the quick and dirty way—in a way that doesn’t make use of npm-specific features or CommonJS modules.

The first approach is similar to most developers’ current practice, sourcing scripts directly and keeping track of dependencies—which scripts depend on which others—in your head. This approach also makes more requests over the network than it needs to, which makes page load times slower, and it hardcodes file names, which means that if you update your library code and files have moved around in that library, these hardcoded references could break.

The second approach, using Browserify to bundle dependencies, made the dependencies explict by using the require function to include code where it’s needed. This makes it easier for a new developer coming on (or you in 6 months) to understand which scripts are used in other parts of the app. Browserify also combines the JavaScript and CSS files, which will help with page load times if you have multiple scripts and CSS files. Unfortunately, we didn’t get away from the problem of hardcoding file names. The paths to tipso’s CSS and JS had to be hardcoded in package.json.

But there is a way to solve this last issue. In the next article, you’ll see how you can improve your own plugins to incorporate npm-specific features and make your code more modular.

Like we said before, there are lots of ways to work with front-end assets. If you have other solutions that work well for you, let us know in the comments or on Twitter.