Rolf B: In „normalem“ JavaScript auf Objekte aus Modulen zugreifen

Beitrag lesen

Hallo Samuel,

ja, so was ähnliches habe ich gerade auch zusammengefrickelt. Wenn man aus einem Modul heraus etwas globales bereitstellen will, muss man es an ein globales Objekt anpappen. Das kann das window-Objekt sein, es könnte auch eine vertrauliche private Eigenschaft an einem HTML Element sein.

Was Du gemacht hast, ist eine zeitliche Abfolge zu erzwingen, über die Millisekundenpause. Wenn das ECMAScript-Modul intern ist, ist das machbar. Kommt es von extern, dürfte das nicht reichen. Jegliche fixe Zeitspanne kann nur falsch sein.

Lass uns dafür das Timing von Modulen anschauen.

  • Normale inline-Scripte werden in dem Moment ausgeführt, wo der Browser sie antrifft. D.h. das HTML Parsing wird unterbrochen, das Script läuft und dann geht das Parsing weiger. Das muss so, weil ein solches Script mit document.write HTML generieren darf.
  • ECMAScript-Module laufen automatisch mit dem defer-Attribut. Das heißt, dass sie genau dann ausgeführt werden, wenn das HTML Dokument fertig geparsed ist. Erst nachdem alle defer-Scripte gelaufen sind, wird DOMContentLoaded gefeuert.
  • Ein ECMAScript-Modul, das DOMContentLoaded nicht verzögern soll, kann das async-Attribut bekommen, dann läuft es frühestens bei Ausführung der defer-Queue und spätestens, wenn es verfügbar ist.

Heißt also:

<script type="module">
   window.test = function() { alert("test"); };
</script>

<script>
   test();
</script>

kann deshalb nicht funktionieren, weil das Modul deferred läuft und die Zuweisung an window.test erst erfolgt, wenn der zweite Scriptblock schon durch ist. Man könnte auf die Idee kommen, dem ECMAScript-Modul eine ID zu geben und sich auf das load-Event des Scriptelements zu registrieren, um zu wissen, wann das Modul da ist - das hilft aber nichts, weil load bei einem inline-Script nicht gefeuert wird. Und ein afterscriptexecute-Event gibt's nicht - nicht offiziell, das ist Firefox-spezial.

Das hier

<script type="module">
   window.test = function() { alert("test"); };
</script>

<script>
   document.addEventListener("DOMContentLoaded", function() {
      test();
   });
</script>

würde dagegen funktionieren, weil DOMContentLoaded erst nach Verarbeitung aller defer-Scripte läuft.

Aber ich mach's trotzdem rot. Ein ECMAScript-Modul, das vorsätzlich Dinge in den globalen Scope ballert, ist nicht im Sinne des Erfinders.

Die sinnvolle Lösung wäre, das ganze Projekt als ECMAScript-Modulbaum zu schreiben, mit vielen kleinen überschaubaren Modülchen, und diese für den Produktivbetrieb zu einer Datei zu bündeln (bspw. mit Webpack).

Rolf

--
sumpsi - posui - obstruxi