document.createElement("script") sincrónicamente

Resuelto Josh Johnson asked hace 14 años • 12 respuestas

¿Es posible llamar a un .jsarchivo de forma sincrónica y luego usarlo inmediatamente después?

<script type="text/javascript">
    var head = document.getElementsByTagName('head').item(0);
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', 'http://mysite/my.js');
    head.appendChild(script);

    myFunction(); // Fails because it hasn't loaded from my.js yet.

    window.onload = function() {
        // Works most of the time but not all of the time.
        // Especially if my.js injects another script that contains myFunction().
        myFunction();
    };
</script>

Esto está simplificado. En mi implementación, el material createElement está en una función. Pensé en agregar algo a la función que pudiera verificar si se creó una instancia de una determinada variable antes de devolver el control. Pero todavía existe el problema de qué hacer al incluir js de otro sitio sobre el que no tengo control.

¿Pensamientos?

Editar:

He aceptado la mejor respuesta por ahora porque da una buena explicación de lo que está pasando. Pero si alguien tiene alguna sugerencia sobre cómo mejorar esto, estoy abierto a recibirla. Aquí hay un ejemplo de lo que me gustaría hacer.

// Include() is a custom function to import js.
Include('my1.js');
Include('my2.js');

myFunc1('blarg');
myFunc2('bleet');

Sólo quiero evitar tener que conocer demasiado los aspectos internos y poder decir: "Deseo usar este módulo y ahora usaré parte del código".

Josh Johnson avatar Jul 14 '10 23:07 Josh Johnson
Aceptado

Puede crear su <script>elemento con un controlador "onload", que se llamará cuando el navegador haya cargado y evaluado el script.

var script = document.createElement('script');
script.onload = function() {
  alert("Script loaded and ready");
};
script.src = "http://whatever.com/the/script.js";
document.getElementsByTagName('head')[0].appendChild(script);

No puedes hacerlo sincrónicamente.

editar : se ha señalado que, fiel a su forma, IE no activa un evento de "carga" en las <script>etiquetas que se cargan/evaluan. Por lo tanto, supongo que lo siguiente que debe hacer sería recuperar el script con XMLHttpRequest y luego eval()hacerlo usted mismo. (O, supongo, introduzca el texto en una <script>etiqueta que agregue; el entorno de ejecución eval()se ve afectado por el alcance local, por lo que no necesariamente hará lo que usted desea que haga).

editar : a partir de principios de 2013 , recomiendo encarecidamente buscar una herramienta de carga de scripts más sólida como Requirejs . Hay muchos casos especiales de los que preocuparse. Para situaciones realmente simples, existe yepnope , que ahora está integrado en Modernizr .

Pointy avatar Jul 14 '2010 16:07 Pointy

Esto no es bonito, pero funciona:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
</script>

<script type="text/javascript">
  functionFromOther();
</script>

O

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  window.onload = function() {
    functionFromOther();
  };
</script>

El script debe incluirse en una <script>etiqueta separada o antes window.onload().

Esto no funcionará:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  functionFromOther(); // Error
</script>

Se puede hacer lo mismo creando un nodo, como hizo Pointy, pero solo en FF. No tiene garantía de cuándo el script estará listo en otros navegadores.

Siendo un purista de XML, realmente odio esto. Pero funciona como era de esperar. Podrías envolver fácilmente esas feas document.write()s para no tener que mirarlas. Incluso podrías hacer pruebas, crear un nodo, agregarlo y luego recurrir a document.write().

Josh Johnson avatar Jul 20 '2010 17:07 Josh Johnson

Esto es muy tarde, pero como referencia futura para cualquiera que desee hacer esto, puede utilizar lo siguiente:

function require(file,callback){
    var head=document.getElementsByTagName("head")[0];
    var script=document.createElement('script');
    script.src=file;
    script.type='text/javascript';
    //real browsers
    script.onload=callback;
    //Internet explorer
    script.onreadystatechange = function() {
        if (this.readyState == 'complete') {
            callback();
        }
    }
    head.appendChild(script);
}

Hice una breve publicación en el blog hace algún tiempo http://crlog.info/2011/10/06/dynamically-requireinclude-a-javascript-file-into-a-page-and-be-notified-when-its -cargado/

zcourts avatar Jan 05 '2013 16:01 zcourts

Las respuestas anteriores me indicaron la dirección correcta. Aquí hay una versión genérica de lo que tengo funcionando:

  var script = document.createElement('script');
  script.src = 'http://' + location.hostname + '/module';
  script.addEventListener('load', postLoadFunction);
  document.head.appendChild(script);

  function postLoadFunction() {
     // add module dependent code here
  }      
James avatar Mar 16 '2016 02:03 James