Как может Вы надежно и динамично загружать файл JavaScript? Это желание может использоваться, чтобы осуществить модуль или компонент это когда ' initialized' компонент динамично загрузит, всем были нужны сценарии библиотеки JavaScript по требованию.
Клиент, который использует компонент isn' t требуемый загрузить все файлы сценария библиотеки (и вручную вставить '< script>'; признаки в их веб-страницу), которые осуществляют этот компонент - просто ' main' составляющий файл сценария.
Как господствующие библиотеки JavaScript достигают этого (Прототип, jQuery, и т.д.)? Сделайте эти инструменты сливают несколько файлов JavaScript в единственный ' без ограничений на свободное распространение; build' версия файла сценария? Или сделайте они делают любую динамическую погрузку вспомогательного ' library' сценарии?
Дополнение к этому вопросу: есть ли способ обращаться с событием после того, как динамично включенный файл JavaScript будет загружен? У прототипа есть 'document.observe' для событий всего документа. Пример:
document.observe("dom:loaded", function() {
// initially hide all containers for tab content
$$('div.tabcontent').invoke('hide');
});
Каковы доступные события для элемента сценария?
Вы можете написать динамические признаки сценария (использующий Прототип):
new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});
Проблема здесь состоит в том, что мы не знаем , когда внешний файл сценария полностью загружен.
Мы часто хотим наш подчиненный кодекс по очень следующей строке и любим писать что-то как:
if (iNeedSomeMore) {
Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
myFancyMethod(); // cool, no need for callbacks!
}
Есть умный способ ввести зависимости от сценария без потребности отзывов. Вы просто должны потянуть сценарий через синхронный запрос AJAX и оценка сценарий на глобальном уровне.
Если Вы используете Прототип, метод Script.load похож на это:
var Script = {
_loadedScripts: [],
include: function(script) {
// include script only once
if (this._loadedScripts.include(script)) {
return false;
}
// request file synchronous
var code = new Ajax.Request(script, {
asynchronous: false,
method: "GET",
evalJS: false,
evalJSON: false
}).transport.responseText;
// eval code on global level
if (Prototype.Browser.IE) {
window.execScript(code);
} else if (Prototype.Browser.WebKit) {
$$("head").first().insert(Object.extend(
new Element("script", {
type: "text/javascript"
}), {
text: code
}
));
} else {
window.eval(code);
}
// remember included script
this._loadedScripts.push(script);
}
};
Нет никакого импорта / включают / требуют в JavaScript, но есть два главных способа достигнуть того, что Вы хотите:
1 - Вы можете загрузить его требованием AJAX, тогда используют оценку.
Это - самый прямой путь, но it' s ограниченный Вашей областью из-за параметров настройки безопасности JavaScript и оценкой использования открывает дверь ошибкам и хакерским проникновениям.
2 - Добавьте признак сценария с URL сценария в HTML.
Определенно лучший способ пойти. Вы можете загрузить сценарий даже от иностранного сервера и it' s убирают, поскольку Вы используете анализатор браузера, чтобы оценить кодекс. Вы можете поместить < сценарий/> признак в главе веб-страницы, или у основания тела.
Оба из этих решений обсуждены и иллюстрированы здесь.
Теперь, есть большая проблема, о которой Вы должны знать. Выполнение, которое подразумевает, что Вы удаленно загружаете кодекс. Современные веб-браузеры будут загружать файл и продолжать выполнять Ваш текущий сценарий, потому что они загружают все асинхронно, чтобы улучшить действия.
Это означает это, если Вы используете эти уловки непосредственно, Вы won' t быть в состоянии использовать Ваш недавно нагруженный кодекс следующая строка после того, как Вы попросили, чтобы он был загружен, потому что это будет все еще загружать.
НАПРИМЕР: my_lovely_script.js содержит MySuperObject
var js = document.createElement("script");
js.type = "text/javascript";
js.src = jsFilePath;
document.body.appendChild(js);
var s = new MySuperObject();
Error : MySuperObject is undefined
Тогда Вы перезагружаете страницу, поражающую F5. И это работает! Путание...
Таким образом, что делать с этим?
Ну, Вы можете использовать хакерское проникновение, которое автор предлагает в связи, которую я дал Вам. Таким образом, для людей второпях, он использует en событие, чтобы управлять функцией обратного вызова, когда сценарий загружен. Таким образом, Вы можете поместить весь кодекс, пользующийся отдаленной библиотекой в функции обратного вызова. НАПРИМЕР:
function loadScript(url, callback)
{
// adding the script tag to the head as suggested before
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// then bind the event to the callback function
// there are several events for cross browser compatibility
script.onreadystatechange = callback;
script.onload = callback;
// fire the loading
head.appendChild(script);
}
Тогда Вы пишете кодекс, который Вы хотите использовать ПОСЛЕ ТОГО, КАК сценарий загружен в функции лямбды:
var myPrettyCode = function() {
// here, do what ever you want
};
Тогда Вы управляете всем этим:
loadScript("my_lovely_script.js", myPrettyCode);
Хорошо, я получил его. Но it' s боль, чтобы написать весь этот материал.
Ну, в этом случае Вы можете использовать как всегда фантастическая свободная структура jQuery, которые позволяют Вам сделать ту же самую вещь в одной линии:
$.getScript("my_lovely_script.js", function() {
alert("Script loaded and executed.");
// here you can use anything you defined in the loaded script
});
Я использовал намного менее сложная версия недавно с jQuery:
<script src="scripts/jquery.js"></script>
<script>
var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
var $head = $("head");
for (var i = 0; i < js.length; i++) {
$head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>");
}
</script>
Это работало отлично в каждом браузере, в котором я проверил его: IE6/7, Firefox, Сафари, Opera.
Обновление: версия jQuery меньше:
<script>
var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
for (var i = 0, l = js.length; i < l; i++) {
document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>");
}
</script>
Я сделал в основном то же самое, что Вы сделали Адама, но с небольшой модификацией, чтобы удостовериться я прилагал к главному признаку, чтобы сделать работу. Я просто создал включать функцию (кодекс ниже), чтобы обращаться и со сценарием и с css файлами.
Эта функция также проверяет, чтобы удостовериться что сценарий или файл CSS hasn' t уже загруженный динамично. Это не проверяет на закодированные ценности руки и, возможно, был лучший способ сделать это, но это служило цели.
function include( url, type ){
// First make sure it hasn't been loaded by something else.
if( Array.contains( includedFile, url ) )
return;
// Determine the MIME-type
var jsExpr = new RegExp( "js$", "i" );
var cssExpr = new RegExp( "css$", "i" );
if( type == null )
if( jsExpr.test( url ) )
type = 'text/javascript';
else if( cssExpr.test( url ) )
type = 'text/css';
// Create the appropriate element.
var tag = null;
switch( type ){
case 'text/javascript' :
tag = document.createElement( 'script' );
tag.type = type;
tag.src = url;
break;
case 'text/css' :
tag = document.createElement( 'link' );
tag.rel = 'stylesheet';
tag.type = type;
tag.href = url;
break;
}
// Insert it to the <head> and the array to ensure it is not
// loaded again.
document.getElementsByTagName("head")[0].appendChild( tag );
Array.add( includedFile, url );
}
другой потрясающий ответ
$.getScript("my_lovely_script.js", function(){
alert("Script loaded and executed.");
// here you can use anything you defined in the loaded script
});
Вот является некоторый пример кода I' ve нашел..., что у кого-либо есть лучший путь?
function include(url)
{
var s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", url);
var nodes = document.getElementsByTagName("*");
var node = nodes[nodes.length -1].parentNode;
node.appendChild(s);
}
Если Вам уже загрузили jQuery, Вы должны использовать $ .getScript.
Это имеет преимущество перед другими ответами здесь в этом, у Вас есть построенный в функции обратного вызова (чтобы гарантировать, что сценарий загружен перед подчиненными кодовыми пробегами) и Вы можете управлять кэшированием.
Просто узнанный о замечательной особенности в YUI 3 (во время написания доступного в выпуске предварительного просмотра). Вы можете легко вставить зависимости в библиотеки YUI и в " external" модули (что Вы ищете) без слишком большого количества кодекса: Погрузчик YUI.
Это также отвечает на Ваш второй вопрос относительно функции, вызванной, как только внешний модуль загружен.
Пример:
YUI({
modules: {
'simple': {
fullpath: "http://example.com/public/js/simple.js"
},
'complicated': {
fullpath: "http://example.com/public/js/complicated.js"
requires: ['simple'] // <-- dependency to 'simple' module
}
},
timeout: 10000
}).use('complicated', function(Y, result) {
// called as soon as 'complicated' is loaded
if (!result.success) {
// loading failed, or timeout
handleError(result.msg);
} else {
// call a function that needs 'complicated'
doSomethingComplicated(...);
}
});
Работавший отлично для меня и имеет преимущество руководящих зависимостей. Обратитесь к документации YUI для пример с календарем YUI 2.
i' ve использовал еще одно решение, которое я нашел в сети..., этот находится под creativecommons, и это проверяет, был ли источник включен до вызывания функции ...
Вы можете найти файл здесь: include.js
/** include - including .js files from JS - [email protected] - 2005-02-09
** Code licensed under Creative Commons Attribution-ShareAlike License
** http://creativecommons.org/licenses/by-sa/2.0/
**/
var hIncludes = null;
function include(sURI)
{
if (document.getElementsByTagName)
{
if (!hIncludes)
{
hIncludes = {};
var cScripts = document.getElementsByTagName("script");
for (var i=0,len=cScripts.length; i < len; i++)
if (cScripts[i].src) hIncludes[cScripts[i].src] = true;
}
if (!hIncludes[sURI])
{
var oNew = document.createElement("script");
oNew.type = "text/javascript";
oNew.src = sURI;
hIncludes[sURI]=true;
document.getElementsByTagName("head")[0].appendChild(oNew);
}
}
}
у кого-либо есть лучший путь?
Я думаю, просто добавив, что сценарий к телу был бы легче тогда добавление его к последнему узлу на странице. Как насчет этого:
function include(url) {
var s = document.createElement("script");
s.setAttribute("type", "text/javascript");
s.setAttribute("src", url);
document.body.appendChild(s);
}
Если Вы хотите СИНХРОНИЗАЦИЯ погрузка сценария, Вы должны добавить текст сценария непосредственно к признаку ГОЛОВЫ HTML. Добавление его как < сценарий src =" myscript.js" > вызовет ASYNC груз. Чтобы загрузить текст сценария от внешнего файла синхронно, используйте XHR. Ниже быстрого образца (это использует части других ответов в этом и других постов):
/*sample requires an additional method for array prototype:*/
if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
var i = this.length;
while (i--) { if (this[i] === obj) return true; }
return false;
};
};
/*define object that will wrap our logic*/
var ScriptLoader = {
LoadedFiles: [],
LoadFile: function (url) {
var self = this;
if (this.LoadedFiles.contains(url)) return;
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
self.LoadedFiles.push(url);
self.AddScript(xhr.responseText);
} else {
if (console) console.error(xhr.statusText);
}
}
};
xhr.open("GET", url, false);/*last parameter defines if call is async or not*/
xhr.send(null);
},
AddScript: function (code) {
var oNew = document.createElement("script");
oNew.type = "text/javascript";
oNew.textContent = code;
document.getElementsByTagName("head")[0].appendChild(oNew);
}
};
/*Load script file. ScriptLoader will check if you try to load a file that has already been loaded (this check might be better, but I'm lazy).*/
ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
/*this will be executed right after upper lines. It requires jquery to execute. It requires a HTML input with id "tb1"*/
$(function () { alert($('#tb1').val()); });
There' s новое предложил стандарт ECMA, названный динамический импорт, недавно включенный в Хром и Сафари.
const moduleSpecifier = './dir/someModule.js';
import(moduleSpecifier)
.then(someModule => someModule.foo()); // executes foo method in someModule
Сохраните его хорошим, коротким, простым, и ремонтируемым!:]
// 3rd party plugins / script (don't forget the full path is necessary)
var FULL_PATH = '', s =
[
FULL_PATH + 'plugins/script.js' // Script example
FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library
FULL_PATH + 'plugins/crypto-js/hmac-sha1.js', // CryptoJS
FULL_PATH + 'plugins/crypto-js/enc-base64-min.js' // CryptoJS
];
function load(url)
{
var ajax = new XMLHttpRequest();
ajax.open('GET', url, false);
ajax.onreadystatechange = function ()
{
var script = ajax.response || ajax.responseText;
if (ajax.readyState === 4)
{
switch(ajax.status)
{
case 200:
eval.apply( window, [script] );
console.log("library loaded: ", url);
break;
default:
console.log("ERROR: library not loaded: ", url);
}
}
};
ajax.send(null);
}
// initialize a single load
load('plugins/script.js');
// initialize a full load of scripts
if (s.length > 0)
{
for (i = 0; i < s.length; i++)
{
load(s[i]);
}
}
Этот кодекс - просто короткий функциональный пример, который мог требовать функциональности дополнительной функции для полной поддержки на любом (или данный) платформа.
Техника, которую мы используем на работе, должна просить файл JavaScript, используя запрос AJAX и затем оценку () возвращение. Если you' ре, пользующееся библиотекой прототипа, они поддерживают эту функциональность в своем Аяксе. Требование запроса.
jQuery решил это для меня с его .append () функция
используемый это, чтобы загрузить полный jQuery ui пакет
/*
ИМЯ ФАЙЛА: project.library.js
ИСПОЛЬЗОВАНИЕ: грузы любая библиотека JavaScript */ вар dirPath = "../js/"; библиотека вара = [" функции js" " swfobject.js" " jQuery jeditable.mini.js" " jQuery ui 1.8.8.custom.min.js\U 0026\quot; " ui/jquery.ui.core.min.js" " ui/jquery.ui.widget.min.js" " ui/jquery.ui.position.min.js" " ui/jquery.ui.button.min.js" " ui/jquery.ui.mouse.min.js" " ui/jquery.ui.dialog.min.js" " ui/jquery.effects.core.min.js" " ui/jquery.effects.blind.min.js" " ui/jquery.effects.fade.min.js" " ui/jquery.effects.slide.min.js" " ui/jquery.effects.transfer.min.js"];
для (сценарий вара в библиотеке) { $ (' head') .append (' < тип сценария =" text/javascript" src =" ' + dirPath + библиотека [сценарий] + ' " > </script> '); }
Использовать - в главе Вашего html/php/etc после того, как Вы импортируете jquery.js, как который Вы просто включали бы этот файл так, чтобы загрузить в полноте Вашей библиотеки, прилагающей его голове...
<script type="text/javascript" src="project.library.js"></script>
Здесь простой с поддержкой IE и отзывом:
function loadScript(url, callback) {
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState) { //IE
script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {
//jQuery loaded
console.log('jquery loaded');
});
Я знаю, что мой ответ - бит поздно для этого вопроса, но, здесь является большой статьей в www.html5rocks.com - Глубоко ныряют в темные воды погрузки сценария.
В той статье приходят к заключению, что в отношениях поддержки браузера, лучший способ динамично загрузить файл JavaScript, не блокируя довольный предоставление - следующий путь:
Рассмотрение you' ve четыре сценария, названные 'script1.js, script2.js, script3.js, script4.js' тогда, Вы можете сделать это с применение async = ложный :
[
'script1.js',
'script2.js',
'script3.js',
'script4.js'
].forEach(function(src) {
var script = document.createElement('script');
script.src = src;
script.async = false;
document.head.appendChild(script);
});
Теперь, Спек говорит : Загрузите вместе, выполните в заказе как только вся загрузка.
Firefox < 3.6, в Opera говорится: Я понятия не имею, какова эта «async» вещь, но это именно так происходит, я выполняю сценарии, добавленные через JS в заказе, они добавлены.
Сафари 5.0 говорит: Я понимаю «async», но не понимаю урегулирование это к «ложному» с JS. Я выполню Ваши сценарии, как только они приземляются в любом заказе.
IE < 10 говорит: Никакая идея о «async», но есть работа, используя «onreadystatechange».
Все остальное говорит: я - Ваш друг, мы собираемся сделать это книгой.
Теперь, полный кодекс с IE < 10 работ:
var scripts = [
'script1.js',
'script2.js',
'script3.js',
'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];
// Watch scripts load in IE
function stateChange() {
// Execute as many scripts in order as we can
var pendingScript;
while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
pendingScript = pendingScripts.shift();
// avoid future loading events from this script (eg, if src changes)
pendingScript.onreadystatechange = null;
// can't just appendChild, old IE bug if element isn't closed
firstScript.parentNode.insertBefore(pendingScript, firstScript);
}
}
// loop through our script urls
while (src = scripts.shift()) {
if ('async' in firstScript) { // modern browsers
script = document.createElement('script');
script.async = false;
script.src = src;
document.head.appendChild(script);
}
else if (firstScript.readyState) { // IE<10
// create a script and add it to our todo pile
script = document.createElement('script');
pendingScripts.push(script);
// listen for state changes
script.onreadystatechange = stateChange;
// must set src AFTER adding onreadystatechange listener
// else we’ll miss the loaded event for cached scripts
script.src = src;
}
else { // fall back to defer
document.write('<script src="' + src + '" defer></'+'script>');
}
}
Несколько уловок и minification позже, это - 362 байта
!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
"//other-domain.com/1.js",
"2.js"
])
Что-то вроде этого...
<script>
$(document).ready(function() {
$('body').append('<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places&callback=getCurrentPickupLocation" async defer><\/script>');
});
</script>
Здесь простой пример для функции, чтобы загрузить файлы JS. Важные моменты:
var getScriptOnce = function() {
var scriptArray = []; //array of urls (closure)
//function to defer loading of script
return function (url, callback){
//the array doesn't have such url
if (scriptArray.indexOf(url) === -1){
var script=document.createElement('script');
script.src=url;
var head=document.getElementsByTagName('head')[0],
done=false;
script.onload=script.onreadystatechange = function(){
if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
done=true;
if (typeof callback === 'function') {
callback();
}
script.onload = script.onreadystatechange = null;
head.removeChild(script);
scriptArray.push(url);
}
};
head.appendChild(script);
}
};
}();
<! - отрывок конца - >
Теперь Вы используете его просто
getScriptOnce("url_of_your_JS_file.js");