简体   繁体   中英

Load script file on demand with vanilla js

I use an accordion or tabs. When clicking on a tab, I want to display a map or something that require a big javascript file to load.

To not load the big javascript when it is not needed, I want to load it only when the tab is clicked.

How can I do that? I've created a good starting point with accordion and a click event.

  • I use vanilla js (pure javascript - no jQuery)
  • I use it on a webpage (not nodejs)
  • I'm fine if it works with modern browsers (IE not needed)

 window.addEventListener('DOMContentLoaded', () => { document.querySelector('[data-trigger]').addEventListener('click', () => { console.log('Load script from file'); // CDN example - https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/1.12.0/mapbox-gl.js }); });
 ul { list-style: none; margin: 0; padding: 0; } label { display: flex; align-items: center; } label:before { content: ''; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath d='M13.172 12l-4.95-4.95 1.414-1.414L16 12l-6.364 6.364-1.414-1.414z'/%3E%3C/svg%3E"); background-repeat: no-repeat; width: 24px; height: 24px; } input[type=checkbox] { display: none; } input[type=checkbox]:checked ~ h2 label:before { transform: rotate(90deg); } p { display: none; } input[type=checkbox]:checked ~ h2 ~ p { display: block; }
 <ul> <li> <input type="checkbox" id="faq-1"> <h2 data-trigger> <label for="faq-1">Click to load map</label> </h2> <p>Gummies marzipan croissant chupa chups.</p> </li> <li> <input type="checkbox" id="faq-2"> <h2> <label for="faq-2">Something else</label> </h2> <p>Cookie bear claw carrot cake croissant.</p> </li> </ul>

You could dynamically create a script tag and add it to the document whenever the button is pressed. This will trigger the script to immediately be loaded. With the onload callback you can run your code, like creating a map, the moment the script has actually loaded.

Do add { once: true } as the third parameter for addEventListener as it will ensure that the trigger can only be clicked once and won't be able to append the script more than once.

function createMap() {
  const map = new mapboxgl.Map({
    ...
  });
}

window.addEventListener('DOMContentLoaded', () => {
  document.querySelector('[data-trigger]').addEventListener('click', () => {
    console.log('Load script from file');
    
    const script = document.createElement('script');
    script.id = 'mapboxgljs';
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/1.12.0/mapbox-gl.js'
    script.async = true;
    script.onload = function() {
      console.log('script loaded, you can use it now.');
      createMap();
    }
    document.body.append(script);
  }, { once: true }); // Make sure that the button can only be pressed once.
});

As you're comfortable with only supporting modern browsers, you could use the dynamic import() function to load the file:

 window.addEventListener('DOMContentLoaded', () => { document.querySelector('[data-trigger]').addEventListener('click', async() => { const module = await import('https://cdnjs.cloudflare.com/ajax/libs/mapbox-gl/1.12.0/mapbox-gl.js'); console.log(`module is loaded: ${Object.keys(mapboxgl)}`); }); });
 ul { list-style: none; margin: 0; padding: 0; } label { display: flex; align-items: center; } label:before { content: ''; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath d='M13.172 12l-4.95-4.95 1.414-1.414L16 12l-6.364 6.364-1.414-1.414z'/%3E%3C/svg%3E"); background-repeat: no-repeat; width: 24px; height: 24px; } input[type=checkbox] { display: none; } input[type=checkbox]:checked~h2 label:before { transform: rotate(90deg); } p { display: none; } input[type=checkbox]:checked~h2~p { display: block; }
 <ul> <li> <input type="checkbox" id="faq-1"> <h2 data-trigger> <label for="faq-1">Click to load map</label> </h2> <p>Gummies marzipan croissant chupa chups.</p> </li> <li> <input type="checkbox" id="faq-2"> <h2> <label for="faq-2">Something else</label> </h2> <p>Cookie bear claw carrot cake croissant.</p> </li> </ul>

import() returns a promise which is resolved on successful fetch, and resolves immediately on subsequent calls (in my testing), so you wouldn't have to deal with that specifically.

For your specific mapbox-gl script, the value returned won't be useful, as the script isn't written as an ES6 module, but the side-effects of the script running will be in place (in this case, the mapboxgl variable being assigned onto globalThis ). In scenarios where the loaded script is a module (using export correctly), you can use the resolved value directly to avoid polluting the global namespace.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM