2009-12-10 3 views
5

У меня есть функция javascript, которую я пишу, которая используется для включения внешнего JS-файла, но только один раз. Причина, по которой мне нужна такая функция, заключается в том, что она вызывается, когда какой-то контент загружается через AJAX, и мне нужно запустить код для этого содержимого (нет, просто использование .live не будет охватывать его).Динамически включая файлы javascript только один раз

Вот моя попытка, укороченная для краткости:

$.include_once = function(filename) { 
    if ($("script[src='" + filename + "']").length === 0) { 
     var $node = $("<script></script>") 
      .attr({ 
       src : filename, 
       type : "text/javascript" 
      }) 
     ; 
     $(document.body).append($node); 
    } 
}; 

Это прекрасно работает: функция вызывается, то он загружает внешний файл, и этот файл уже выполняется при загрузке. Отлично.

Проблема в том, что он всегда будет перезагружать этот внешний файл: запрос, который я использую, чтобы проверить наличие скрипта, не находит ничего!

При отладке этого, я добавил несколько строк:

alert($("script").length);  // alerts: 4 
$(document.body).append($node); 
alert($("script").length);  // alerts: 4 

прогностических в динамическом источнике (вкладка HTML в Firebug), я не могу найти тег сценария на всех.

Я знаю, что я мог бы поддерживать массив файлов, которые я ранее включил, но я надеялся пойти с таким методом, который (если он сработал) кажется немного более надежным, поскольку не все файлы JS включены таким образом.

Может ли кто-нибудь объяснить поведение, наблюдаемое в этом втором фрагменте?

ответ

5

jQuery в этом случае немного глуповат; это вовсе не значит, что вы ожидаете. Когда вы append($node) JQuery делает это:

jQuery.ajax({ 
    url: $node.src, 
    async: false, 
    dataType: "script" 
}) 

Woops! Для локальных файлов (например, в том же домене) jQuery выполняет стандартный XMLHttpRequest для тела файла .js и переходит к «eval» его целым запутанным процессом создания тега <script> (снова!), А его настройки - . Содержимое вашего тела .js. Это должно имитировать eval, но в глобальном контексте.

Для файлов между доменами, так как он не может выполнить стандартную XMLHttpRequest из-за политики же домена, JQuery вновь создает <script> элемент и вставляет его в <head>.

В обоих локальных и кросс-доменных случаях выше JQuery наконец получает вокруг, чтобы сделать это:

head.removeChild(script); 

И боны ваш .length чек! Облом.

Так что к вашей проблеме, не беспокойтесь о jQuery с этим. Просто сделайте

document.getElementsByTagName('head')[0] 
    .appendChild(
    document.createElement('script') 
) 
    .src = filename; 

Что будет делать то, что вы ожидаете, в частности, запрашивать его позже.

+0

Отличный отклик. Я полагал, что jQuery был слишком умным для себя или чего-то другого. – nickf

+0

@nickf: Я чувствую себя так, как большинство из встроенных jQuery (будучи слишком умным, что есть). Там, наверное, есть много необходимых зол. –

3

Вы пытаетесь решить проблему, которая уже была решена несколько раз. Попробуйте, например, LazyLoad. Также есть похожие плагины для jQuery.

0

Вместо установки атрибута источника тега-скрипта установите атрибут «текст» тега сценария. Это работает во всех современных браузерах (приложение, в котором я использую это на практике, не поддерживает IE6, поэтому я не знаю об этом ползучести ...).

На практике это будет выглядеть так (вам нужно добавить код, чтобы опустить двойное включение на себя - например, простой массив из всех загруженных скриптов, хотя это очень специфично для приложений, и почему вы все равно загружаете код дважды? пропустить двойной загрузки кода ...):

var script_source_code_string = <some dynamically loaded script source code>; 
var $n = $("<script></script>"); 
$n.get(0).text = script_source_code_string; 
$(document.body).append($n); 

Или еще проще (без JQuery, мой код на этом этапе не знает JQuery, он также может быть загружен динамически):

var script_source_code_string = <some dynamically loaded script source code>; 
var s = document.createElement('script'); 
document.getElementsByTagName('head')[0].appendChild(s); 
s.text = script_source_code_string; 
Смежные вопросы