DEV Community

Cover image for How I updated my open source plugin from JQUERY to plain JS
Orion3D
Orion3D

Posted on

How I updated my open source plugin from JQUERY to plain JS

I've been wanting to post something for a while but have never really done it before, it's quite scary. Anyway, let's dive into it!


A while back I created a simple plugin to recreate the google images sliding effect. It turned out to be rather popular (> 450 stars) and evolved quite a bit from its infancy.

Recently, I've been getting messages asking to remove JQUERY as a dependency or if there was a JS version. I thought it would be interesting/challenging to test myself to redo it with the least amount of dependencies possible, and in plain javascript.


You can find the result here: GridderJS

And here is a demo: Demo


Overall features

First I had to plan what features I wanted to keep/update:

  • Multiple instances on the same page
  • Easy to use and customize with CSS & HTML
  • Any number of columns
  • Expanding preview with static HTML content or dynamic (fetch) HTML content
  • Responsive
  • Smooth Scrolling
  • Old / New Google Images display (expander underneath grid or as a sidebar on the right-hand side)
  • Lightweight
  • Work out of the box with zero user CSS needed
  • Multiple themes
  • Plays well with other plugins

Things I learned/struggled with


Website I used heavily

I'm not very familiar with using javascript for dom manipulation, and this website made it very easy to do just that.


Parcel.js is awesome

Using parcel.js made it a breeze to set up a build process directly in my package.json file as seen in the snippet below. It generates the plugin as a ES6 module & CommonJS module & a standalone js file. Everything is minified and compressed automatically. I barely scratch the surface of what's is capable of doing, and cannot wait to use it again.

 {
  "source": "src/gridder-js.js",
  "main": "dist/gridder-js.js",
  "module": "dist/gridder-js.mjs",
  "standalone": "dist/gridder-js-min.js",
  "targets": {
    "main": {
      "source": "src/gridder-js.js"
    },
    "module": {
      "source": "src/gridder-js.js"
    },
    "standalone": {
      "source": "tool/gridderjs-global.js",
      "outputFormat": "global",
      "optimize": true
    },
    "demo": {
      "source": "demo/demo.scss"
    }
  },
  "scripts": {
    "watch": "parcel watch",
    "build": "parcel build"
  },
}
Enter fullscreen mode Exit fullscreen mode

How I dealt with the grid

Google Images now show expander on the right-hand side instead of underneath the current row of the selected image so I had to build in an option to toggle between each layout which was not as simple as I originally thought. But with a minimum amount of if's and else + minimum amount of CSS, it's now working quite well,

Basic columns
Via the columns option, it's very easy to specify how many columns you want. This was easily achieved using CSS grids

display: 'grid';
grid-template-columns: 'repeat('+this.options.columns+', 1fr)';
Enter fullscreen mode Exit fullscreen mode

Display Bottom - Old style
The important thing on this one was to have the below on the parent element

display: 'grid'; 
grid-auto-flow: 'row dense';
grid-template-rows= 'min-content';
Enter fullscreen mode Exit fullscreen mode

Display Right - New style
For this option, I had to restructure the JS to allow appending a sidebar at 30% while reducing the main content to 70%. Additionally, I added the below to the sidebar to make it sticky:

position: sticky; 
min-height: 100vh;
Enter fullscreen mode Exit fullscreen mode

I could not find a way to have a right-hand sidebar within the CSS grid


Private & Public functions

Thanks to the addition of Private class features, I can have a #functionName, and call it with this.#functionName.

I could very easily expose any public function to users, in my case, I added an update(options) & destroy() method.


Adding a responsive option was hard

Previously, I was letting the user use CSS media queries to achieve whatever grid they wanted, but many people wanted a more straightforward option

Finding an easy way to implement responsiveness turned out to be rather difficult, however, I'm pretty happy with the results I came up with.

{
    // default options
    columns: 4,
    gap: 15,

    // responsive breakpoints 
    // must be from highest to lowest width
    breakpoints: {
        960: {
            columns: 4,
            gap: 15,
        },
        700: {
            columns: 3,
            gap: 5,
        },
        400: {
            columns: 2,
            gap: 5,
            display: 'bottom',
        }
    },
}
Enter fullscreen mode Exit fullscreen mode

With the information above I create an array of breakpoints available, then I have a listener that triggers on window resize, finds the closest breakpoint, and call an update() with the provided options.

// Resize Event capture every 500ms
var resizeTimer;
window.addEventListener('resize', function(e){
  clearTimeout(resizeTimer);
  resizeTimer = setTimeout(function() {
    GridderJS.resizeGridder();     
  }, 500);  
});


// Resize all gridder instances.
// a bit ugly, but works: to be refactored once I find a better way to make it work. suggestions, anyone?
GridderJS.resizeGridder = function () {
  // get window width  
  let wWidth = window.innerWidth;

  // iterate through each instance
  GridderJS.instances.forEach((gridder) => {

    // if no additional breakpoint is specified, then skip
    if(gridder.breakpoints.length < 2) return false;

    // else iterate through all breakpoint options
    for(const n in gridder.breakpoints) {

      // skip first
      if(n > 0){
        let breakpoint = gridder.breakpoints[n];

        // update 
        if(wWidth <= breakpoint[0]){
          gridder.update(breakpoint[1]);
          break;
        }
      }

      // if all else fails use the default
      gridder.update(gridder.breakpoints[0][1]);
    };

  });
};
Enter fullscreen mode Exit fullscreen mode

Working with other plugins

I've added a few callbacks which allow users to easily interact with other plugins, the demo link shows how I use lazyload.js to lazy load all images.

// just call the init function passed in the user options
this.call.option.init();
Enter fullscreen mode Exit fullscreen mode
// init callback
var gridder = new GridderJS('.gridder', {
    'columns': 8,
    'gap': 15,
    'init': function(el){
        var myLazyLoad = new LazyLoad({
            container: this.element
        });
    },
});

// update callback
gridder.update({ 'columns': 4 });

// destroy callback
gridder.destroy();
Enter fullscreen mode Exit fullscreen mode

Things I'm happy with

  • Overall features were all implemented
  • Nearly zero dependencies (just-extend)
  • Performant
  • Lightweight (2.6 kB gzipped)

Ending Notes

That's it, we went through some of the things I struggled with & things I learned along the way.

I could probably improve the plugin massively and refactor it to infinity, but I'm very happy with the current result and performance.

I hope you enjoyed reading this article.

Cheers,
Orion

Top comments (0)