Frontend Development

You can write better jQuery: A DRY approach to multiple configurations

Nick Ciolpan

Nick Ciolpan

While almost every new article mentioning it, talks about its demise, I’ve decided to write about one of the most recurrent jQuery bad practices I came across. So, here it is:

jQuery WET code - Don't try this at home

    
      
              $('.multiSelectOne').multiselect({
                buttonClass: 'btn btn-default',
                buttonWidth: '100%',
                ...
              });

              $('.multiSelectTwo').multiselect({
                buttonClass: 'btn btn-default',
                buttonWidth: '100%',
                nonSelectedText: '---',
                ...
              });
              
    
  

Once upon a time, while working with a new team of full-stack web developers, I’ve discovered a recurring pattern: The developer created a different class name for each new element with a slightly different configuration. I’ve approached this issue having three principles in mind:

  • HTML verbosity is cheaper than Javascript verbosity
  • Don’t repeat yourself (DRY)
  • Open for extension, closed for modification

To avoid repetition, we’ll define a defaults object which we will extend later on with extra options.

jQuery default settings object

    
      
              // I'm keeping .class and $selector separated for edge cases in which dom-content is dynamic and can't be cached.
              const $multiSelect = $('.js-multiselect'),
                defaults = {
                  buttonClass: 'btn btn-default',
                  buttonWidth: '100%',
                  nonSelectedText: '---',
                  nSelectedText: ' selected',
                  allSelectedText: 'All',
                  includeSelectAllOption: false,
                  numberDisplayed: 1,
                  enableFiltering: false,
                  enableCaseInsensitiveFiltering: false
                };
                
    
  

The source of truth will be the HTML element itself. HTML verbosity is cheap, and what I mean by this, is that no matter how many lines of it gets loaded, it takes place in the initial page load. Also, the element makes a full disclosure of what to expect of it when the script steps in.

HTML tags with settings props

    
      
              <select class="js-multiselect">
                <option />
              </select>

              
              <select class="js-multiselect" data-include-select-all-option="true" data-all-selected-text="All">
                <option />
              </select>
              
    
  

By moving the options to the element we have eliminated the need for different selectors and separated initializations.

Initialize with custom props or fallback to defaults

    
      
              // The source of truth should be the element that initializes it.
              // Have a default fallback

              if ($multiSelect.length > 0) {
                  $multiSelect.each(function () {
                    let $this = $(this),
                      dataset = $this.data(),
                      options = defaults;

                  // Since the number of options can vary,
                  // non vital options that have no specified default
                  // will be extracted from the dataset and added to options

                  Object.entries(dataset)
                    .map(property => options[property[0]] = property[1]);

                  // We initialize the plugin with its
                  // specific options for each instance

                  $this.multiselect(options);
                });
              }
              
    
  

Conclusion

jQuery is often misused because it’s so easy to use in the first place. The insane development velocity it gives you at the beginning of the project, makes you easily forget about code standards. The snippet above provides an easy to understand example of how you can architect jQuery, so your codebase stays easy to extend and to maintain.

Your frontend development experts.

Let’s build it!