1

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?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • What is it your trying to do?, first problem I can see -> `const productTabs = document.querySelectorAll('.js-product-tab');` That's going to get called before the DOM is even loaded,. – Keith Jun 20 '18 at 21:58
  • that is the problem. Should i change those variables the be created inside the functions that use them? – ricardogoldstein Jun 20 '18 at 22:13
  • I would just move your consts into your `init`.. seen as init is been called when the DOM is ready. – Keith Jun 20 '18 at 22:15
  • I cant do that because that are more functions inside the Product module that use those consts. Would be undefined if i move them – ricardogoldstein Jun 20 '18 at 22:19
  • You don't really need IIFEs when using a module pattern. The point of an IIFE is to avoid polluting global scope, but modules have their own scope and you can only affect global scope by deliberatly hanging things off `window` (or `global` in NodeJS) – Duncan Thacker Jun 20 '18 at 22:29
  • Thanks for your answer @DuncanThacker. Even if a remove those IFFEs the entire code would run getting errors in those variables, right? How can i prevent that? – ricardogoldstein Jun 20 '18 at 22:32

1 Answers1

2

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)

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • I think you want `product.init()` inside the `DOMContentLoaded` handler...? – Heretic Monkey Jun 20 '18 at 22:12
  • I didnt got this part `that way, initial flow control can lie entirely with an entry point (if you don't have one, hopefully you can create one),`. Can you explain it? Thanks for the response. – ricardogoldstein Jun 20 '18 at 22:17
  • Yes, i do. But i only want the Product module to run in a certain page, but when i import the module, those variables and everything else is created. – ricardogoldstein Jun 20 '18 at 22:18
  • @ricardogoldstein That is, your script should ideally have a *single* entry point that can import and call everything else, as needed. For example, your `init` module could be your entry point here - you can have it call `makeProduct` and other modules as necessary, with all effects spreading outward from the initial `init` module. – CertainPerformance Jun 20 '18 at 22:49
  • oh, i got it. I have and index.js where i import all my modules and init them. My problem is that the variables are created as i import my modules. Should i load them inside the functions that use them? – ricardogoldstein Jun 20 '18 at 22:59
  • @ricardogoldstein Yes, definitely! Import *functions* and then call them whenever possible, rather than importing modules that run code on their own when imported, and the control flow of your script will become much easier to manage. Eg. in your `index.js` you could check the page that you're on and call other module functions as needed (like `if (onProductPage) initProduct()` where `initProduct` is the module with `init` – CertainPerformance Jun 20 '18 at 23:03
  • thanks @CertainPerformance! I`m kind a newbie structuring the whole project and using design patterns. – ricardogoldstein Jun 20 '18 at 23:11