-1

I want to use the following code in an HTML document:

<html>
<script type="module">
   import Dms from 'https://cdn.jsdelivr.net/npm/[email protected]/dms.js';                 
    alert(Dms.toLat(-3.62, 'dms'));
</script>

<script type="text/">
    alert(Dms.toLat(-3.62, 'dms'));
</script>
</html>

The first alert works fine, but the second one in type="text/javascript" does not because it is out of scope. How can I call the Dms class from the second script?

I also tried

<script type="module">
   import  Dms  from './geodesy/dms.js';             
   alert (Dms.toLat(-3.62, 'dms'));
   window.dms = Dms;
</script>

<script type="text/javascript">
   alert (dms.toLat(-3.62, 'dms'));
</script>

After all I use

    <script>

  (async () => {
    
    const {default: Dms} = await import('https://cdn.jsdelivr.net/npm/[email protected]/dms.js');
    alert(Dms.toLat(-3.62, 'dms'));
  
  })();

</script>

But now I have the next problem: how to use a second module? I want to use

import Mgrs, { LatLon } from 'geodesy/mgrs.js';

(see https://github.com/chrisveness/geodesy)

How must the const declaration must look for it?

I'm not firm with this.

6
  • 2
    Don't use the type attribute for anything other than "module", or when you explicitly want to make the <script> block be ignored (with some random not-real type). Commented Jun 16, 2023 at 15:05
  • 2
    Also you will be running into the problem that module scripts are implicitly deferred, while your plain script is not. Thus when your plain script runs, even if you do the explicit window.dms export, the symbol will be missing because the module script won't have finished yet. You can (probably) make the plain script a module also, which might work. Commented Jun 16, 2023 at 15:14
  • Why do you need a second script? Just put all the code in a single module script. Commented Jun 16, 2023 at 16:44
  • Thank you. But if I not type="text/javascript" or use only one type="module"-Block the other code being in <script type="text/javascript"> will not run. For example body onload="initialize()"> results in "Uncaught ReferenceError: initialize is not defined" Commented Jun 16, 2023 at 17:16
  • Yes. Use document.body.onload = initialize; from inside the module scope instead. Commented Jun 16, 2023 at 17:22

1 Answer 1

1

A window property setter solution:

<script type="module">
   import Dms from 'https://cdn.jsdelivr.net/npm/[email protected]/dms.js';                 
   alert(Dms.toLat(-3.62, 'dms'));
   window.Dms = Dms;
</script>

<script>
  let Dms;
  Object.defineProperty(window, 'Dms', {
    get(){
      return Dms;
    },
    set(val){
      Dms = val;
      window.dispatchEvent(new Event('dms-set'));
    }
  });
  
  window.addEventListener('dms-set', () => alert('SECOND TIME: ' + Dms.toLat(-3.62, 'dms')));
  
</script>

Some promise solution:

<script type="module">
   import Dms from 'https://cdn.jsdelivr.net/npm/[email protected]/dms.js';                 
   alert(Dms.toLat(-3.62, 'dms'));
   window.Dms = Dms;
</script>

<script>

  promisifyGlobal('Dms');
  
  Dms.then(Dms => alert('SECOND TIME: ' + Dms.toLat(-3.62, 'dms')));

  function promisifyGlobal(name){
    let global, promise = new Promise(resolve => {
      Object.defineProperty(window, name, {
        get(){
          return promise;
        },
        set(val){
          global = val;
          resolve(global);
        }
      });
    });
  }

</script>

You can easily import dynamically into a non-module script:

<script>

  (async () => {
    
    const {default: Dms} = await import('https://cdn.jsdelivr.net/npm/[email protected]/dms.js');
    alert(Dms.toLat(-3.62, 'dms'));
  
  })();

</script>

And finally some crazy generic solution:

<script type="module">

  import Dms from 'https://cdn.jsdelivr.net/npm/[email protected]/dms.js'; 
  alert(Dms.toLat(-3.62, 'dms'));

</script>

<script>

  // this script will handle all imports 
  // tweak to import several exports and named exports

  for(const script of document.querySelectorAll('script[type=module]')) {
    
    const regex = /import\s+([^\s]+)\s+from\s+["\']([^"\']+)/g;
    let matches;
    while(matches = regex.exec(script.textContent)){
      const [, name, url] = matches;
      const src = `import ${name} from '${url}'; export default ${name};`;
      window[name] = new Promise(resolve => {
        import(`data:text/javascript, ${src}`).then(({default:imported}) => {
          setTimeout(() => resolve(imported));
        });
      });
    }

  }

</script>

<script>

  Dms.then(Dms => alert('SECOND TIME: ' + Dms.toLat(-3.62, 'dms')));

</script>

Sign up to request clarification or add additional context in comments.

5 Comments

It's really confusing to have Dms and window.Dms do different things. Why not simply fire the event from the module?
@Bergi i guess it's rather an experiment. also the workflow could be generalized like i did in the second promise solution
@Bergi added in the end a more generic solution. it's all pure experimental
The async-solution work. jsfiddle.net/wtc1089u But I'm wondering: why I'm the only person with this problem?
@Krubkjlkjlökg if the async solution works for you you could accept my answer (the checkbox) and upvote it (the arrow up). that way i'll get points and will be motivated to help you in the future

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.