javascriptecmascript-6revealing-module-pattern

Make imported code run only in specific page [Module Pattern + ES6]


I'm developing a code structure with import/export from ES6 using the module pattern. So in a file I have:

// Product.js

const Product = (function product() {
   const productJson = skuJson;
   const productTabs = document.querySelectorAll('.js-product-tab');
   const handleClickProductTab = (evt) => {...};
   const init = () => {
     [...productTabs].forEach((tab) => {
       tab.addEventListener('click', handleClickProductTab);
     });
   };
   return {
     init,
   };
}());

export default Product;

And in another file I import this Product js file for future use.

import Product from './product/product';

(function init() {
  document.addEventListener('DOMContentLoaded', () => {
    Product.init();
  });
}());

I understand that the code runs because of the IIFE, and even if a remove the Product.init(); the variables would still be created. But that's the thing, I get errors from pages that those modules should not be read.

How can I get those variables created in every module to only be created when I call the init() method but those variables still be available to the rest of the module?


Solution

  • It's often good to export functions that can be initialized on demand rather than to automatically run each module's function on page load - that way, initial flow control can lie entirely with an entry point (if you don't have one, hopefully you can create one), rather than initial actions being scattered throughout your scripts.

    For example, you might consider exporting a function makeProduct that creates Product when run:

    // makeProduct.js
    
    const makeProduct = () => {
       const productJson = skuJson;
       const productTabs = document.querySelectorAll('.js-product-tab');
       const handleClickProductTab = (evt) => {...};
       const init = () => {
         [...productTabs].forEach((tab) => {
           tab.addEventListener('click', handleClickProductTab);
         });
       };
       return {
         init,
       };
    };
    
    export default makeProduct;
    

    And then

    import makeProduct from './product/makeProduct';
    
    (function init() {
      const product = makeProduct();
      document.addEventListener('DOMContentLoaded', () => {
        product.init();
      });
    })();
    

    (of course, if the product initialization depends on DOMContentLoaded, create it inside the DOMContentLoaded listener)