Kruse-Net.dk

Det man blogger er man selv…

Include/import i Javascript

Med Web 2.0 og de deraf følgende Javascript biblioteker af stigende størrelse er der specielt én mangel i Javascript der så småt begynder at blive kritisk: mangelen på en “include” eller “import” erklæring, eller med andre ord at man ikke umiddelbart i én script fil kan angive at en anden skal indlæses. Min forrige artikel, “Leg med AJAX”, illustrerer det kun alt for tydeligt. Der skal inkluderes ikke mindre end 8 forskellige biblioteker for at få stillet funktionaliteten i det sidste til rådighed. Den stakkels udvikler der skal bruge Google biblioteket skal selv holde styr på at få de andre 7 inkluderet i den rette rækkefølge. Ekstrapoleres eksemplet til en hel Web 2.0 applikation kan det være over 100 script filer der på den måde skal holdes styr på. Noget må gøres!

Løsningen er nærliggende. AJAX giver netop mulighed for ikke blot at hente XML, men også Javascript og alt muligt andet. Så de samme teknologier som anvendes til at gøre en Web 2.0 side levende kan anvendes til at forenkle den. Løsningen findes i flere forskellige varianter. De fleste inkluderer på en eller anden måde eval() funktionen, hvilket jeg har meget svært ved at finde elegant – den har aldrig samme effekt som at loade filen på den “sædvanlige” måde, med et script tag. Den løsning jeg foreslår her er lidt anderledes, omend uhyre enkel: den efterspurgte Javascript kode hentes som tekst og indsættes i kroppen af et script tag, der derefter indsættes på siden. Browserens umiddelbare reaktion er at fortolke hele indholdet af script tag’et.

Nedenstående to knapper illustrerer teknikken. Klik på den første og du får en fejl (eller ingenting, hvis din browser ikke er sat op til at vise Javascript fejl). Klik på den anden medfører indlæsning af et script, hvorefter klik på første knap igen har et mere positivt resultat.

Script objektet der stiller løsningen til rådighed findes i filen Script.js. Det er ganske få linier der er tale om, så overhead’et er lille. Script objektet stiller udover funktionen require() også en funktion addAlias() til rådighed. Tanken er at en script fil der gør brug af Script.require() ikke nødvendigvis skal rettes blot fordi der flyttes rundt på de andre script-filer. Ved at referere til symbolske navne i stedet for absolutte stier er dette enkelt at håndtere. I praksis tænkes biblioteket anvendt på følgende måde på en given side:

<!-- Først loader vi Script biblioteket -->
<script type="text/javascript" src="Script.js"></script>

<!-- Derefter sætter vi alle nødvendige alias'er op (bør placeres i en ekstern script fil) -->
<script type="text/javascript">
  Script.addAlias("Prototype 1.4", "/javascript/lib/prototype.js");
  Script.addAlias("Prototype 1.5", "/javascript/lib/prototype-1.5.0_pre0.js");
  Script.addAlias("String extensions", "/javascript/ext/StringX.js");
  // ...
</script>

<!-- Så er vi klar! Nu kan vi inkludere script filer der indeholder noget svarende til: -->
<script type="text/javascript">
  Script.require("String extensions");
  
  alert("foobar".right(3)); // right funktionen defineres i String extensions
</script>

Der hører et par bemærkninger til løsningen her til sidst. Eftersom XmlHttpRequest anvendes til at hente de ønskede scripts ligger løsningen under for cross domain problematikken – det er kun muligt at bede om scripts fra samme server som siden kommer fra. Funktionen `require()` har en ekstra parameter “async” man kan sætte til “true” for at omgå dette problem. Resultatet er at der blot indsættes et script tag med en src attribut. Det har samme virkning, men som async parameteren angiver hentes scriptet asynkront, dvs. mens det eksekverende script kører videre. Alt hvad der loades på denne måde vil således først være tilgængeligt når den aktuelle script blok forlades (alle de browsere jeg har testet venter med at starte eksekveringen af næste script blok til de ønskede data er hentet). I nogle tilfælde er det brugbart.

Opdatering: Det viser sig at når scripts hentes synkront (via XmlHttpRequest) i Internet Explorer så kan der meget let opstå encoding problemer. Hvis web serveren afsender script filen uden angivelse af encoding (hvilket fortolkes som UTF-8), og filen f.eks. indeholder ikke-US-ASCII tegn kodet i ISO-LATIN1 så går det galt. Jeg tror dette er forklaringen på kommentar nummer 2 nedenfor. Problemet kan, hvor jeg har testet, løses ved at sikre at filen der hentes er kodet i UTF-8.

Bemærk at samme fil hentet “på normal vis” (ved et script tag) fortolkes korrekt af Internet Explorer.

Opdatering: Internet Explorer demonstrerer også problemer hvis ét script, der er hentet asynkront, beder om at hente et andet script synkront. Så af hensyn til Internet Explorer bør man hente alle scripts synkront.

7 Responses på “Include/import i Javascript”

  1. gravatar 1 Kim Friis
    22. marts 2006 kl. 09:57

    Hmm ganske smart og brugbart. Det må jeg lige se om jeg ikke kunne få brug for senere når jeg får lavet noget mere ajax programmering.

  2. gravatar 2 Jan Morgils
    4. april 2006 kl. 10:34

    Ang. script.require. Det er fint ud i Mozilla, men giver fejl i IE 6.

  3. gravatar 3 Demez Christophe
    28. november 2006 kl. 09:19

    Hi

    I think that there is an error in your script :

    REPLACE : if (async) { script.src = src; …

    WITH: if (!async) { script.src = src; …

    Right ?

  4. gravatar 4 Jakob Kruse
    28. november 2006 kl. 09:38

    No, actually the code is correct. “script” elements load their content asynchronously. Because they don’t have cross-domain limitations I use them when possible. The XHR object has cross-domain limitations, but is the only way to load content synchronously (with the third parameter ‘false’).

  5. gravatar 5 Michael Schøler
    4. juli 2008 kl. 19:32

    Fejler også i seneste IE7. Æv, havde ellers lige brug for det.

  6. gravatar 6 Miklas
    17. november 2008 kl. 23:22

    Ej! – troede også lige at jeg havde fundet den hellige gral, Men din js funger ikke i safari 1.5 på Mac. Har ledt hele dagen efter en måde at hente en simpel text fil ind i et javascript, men der er ikke noget der vil lykkes.

  7. gravatar 7 Jakob Kruse
    18. november 2008 kl. 10:16

    Det beklager jeg naturligvis meget. Men nu er Safari 1.5 jo også noget forældet, er den ikke? Jeg har desværre ikke adgang til en Safari 1.5 at teste på, så mine muligheder for evt. at finde en løsning er ret begrænsede.

    I øvrigt må jeg lige korrigere ovenstående kommentar om IE7. Script.js fungerer fint i IE7, så der må have været tale om en lokal fejl.

Skriv et svar