Omar AL Zabir这位MVP总是喜欢搞些稀奇古怪同时又很实用的小东西,并且还十分值得参考。最近他就做了一个叫做ensure的小工具用于动态加载JavaScript、CSS与HTML,而且IE、Firefox、Opera、Safari都支持了,那么我们就来看看ensure是如何做到动态加载JavaScript与CSS的。
在介绍ensure内部的实现之前,让我们先来看看其功能:
ensure({
html: "popup.html",
javascript: "popup.js",
css: "popup.css"
}, function() {
Popup.show("hello world");
}
);
在这段代码中,ensure首先会确保popup.html、popup.js、popup.css这3个文件的加载,如果都没加载过ensure就会动态加载它们;如果已经加载过了,ensure不会再次加载。在确保这3个文件都加载后,ensure会调用后面的匿名函数,也就是执行Popup.show("hello world");。
接下来,就让我们看看ensure是如何动态加载JavaScript与CSS的。
加载JavaScript
在ensure当中,加载JavaScript分两种情况来执行,也就是Safari与非Safari这两种情况。
在IE、Firefox、Opera中加载JavaScript
在这三款浏览器中加载JavaScript,其实只需要创建一个script元素,把src指向要加载的URL,最后把script元素追加到head元素上,那就搞掂了。此项工作是在HttpLibrary.createScriptTag()中完成的。不过我们不仅仅要加载JavaScript,同时还需要知道它什么时候完成加载,这可以通过script元素的onload事件或onreadystatechange事件来实现。
在Safari中加载JavaScript
因为Safari 2不支持onload或者onreadystatechange,所以只能手动通过XHR把URL读去过来,然后再手动eval这段代码,这就带来了一个限制──只能加载本域的JavaScript文件。在ensure当中,eval的工作是通过HttpLibrary.globalEval()来完成的。为了让JavaScript代码在全局(global)上下文中eval,ensure还是使用了创建script元素的方法,并将要eval的JavaScript置于其内,最后把script元素追加到head元素内。
细心的人肯定要问,为什么HttpLibrary.globalEval()要如此设计,而非直接window.eval或者eval.call。这是因为,window.eval和eval.call都无法在IE6中实现和script标签加载JavaScript代码一模一样的效果,这两种做法的eval在IE6下仍然不是在全局上下文中执行的。搜索一下你就会发现一些相关的讨论,例如jQuery就曾经使用window.execScript()来完成此项任务。不过最终大家都发现添加script元素才是最好的跨浏览器解决方案,所以现在的jQuery和ensure都是如此实现的了。
加载CSS
相对于加载JavaScript而言,加载CSS就简单多了,而且方法也是类似的:在head元素内直接加入link元素就可以了。这也正是loadCSS()所完成的工作。
实际上,ensure没有确保CSS完成加载后再执行下去。这估计是因为浏览器都能够在CSS加载完成后自动应用到页面上,因此Omar AL Zabir就认为CSS的加载顺序是无关紧要的,不过假如CSS加载速度实在太慢,其实还是会影响显示效果的。
在IE6中加载CSS
这次需要特别照顾的是IE6,而非Safari。IE6在往head元素添加link元素时,必须在window的上下文中完成,因此添加link的函数通过call调用切换了上下文。
总结
实际上动态加载JavaScript与CSS都并不难,在大多数情况下只需要向head元素追加对应的子元素就可以了,只有Safari2和IE6这两款古老的浏览器是需要特殊照顾的。
/*
Script: Ensure.js
Ensure library
A tiny javascript library that provides a handy function "ensure" which allows you to load
Javascript, HTML, CSS on-demand and then execute your code. Ensure ensures that relevent
Javascript and HTML snippets are already in the browser DOM before executing your code
that uses them.
To download last version of this script use this link:
Version:
1.0 - Initial release
Compatibility:
FireFox - Version 2 and 3
Internet Explorer - Version 6 and 7
Opera - 9 (probably 8 too)
Safari - Version 2 and 3
Konqueror - Version 3 or greater
Dependencies:
Credits:
- Global Javascript execution -
Author:
Omar AL Zabir - http://www.php.cn/
License:
>Copyright (C) 2008 Omar AL Zabir - http://www.php.cn/
>
>Permission is hereby granted, free of charge,
>to any person obtaining a copy of this software and associated
>documentation files (the "Software"),
>to deal in the Software without restriction,
>including without limitation the rights to use, copy, modify, merge,
>publish, distribute, sublicense, and/or sell copies of the Software,
>and to permit persons to whom the Software is furnished to do so,
>subject to the following conditions:
>
>The above copyright notice and this permission notice shall be included
>in all copies or substantial portions of the Software.
>
>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
>INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
>IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
>DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
>ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
>OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
(function(){
window.ensure = function( data, callback, scope )
{
if( typeof jQuery == "undefined" && typeof Sys == "undefined" && typeof Prototype == "undefined" )
return alert("jQuery, Microsoft ASP.NET AJAX or Prototype library not found. One must be present for ensure to work");
// There's a test criteria which when false, the associated components must be loaded. But if true,
// no need to load the components
if( typeof data.test != "undefined" )
{
var test = function() { return data.test };
if( typeof data.test == "string" )
{
test = function()
{
// If there's no such Javascript variable and there's no such DOM element with ID then
// the test fails. If any exists, then test succeeds
return !(eval( "typeof " + data.test ) == "undefined"
&& document.getElementById(data.test) == null);
}
}
else if( typeof data.test == "function" )
{
test = data.test;
}
// Now we have test prepared, time to execute the test and see if it returns null, undefined or false in any
// scenario. If it does, then load the specified javascript/html/css
if( test() === false || typeof test() == "undefined" || test() == null )
new ensureExecutor(data, callback, scope);
// Test succeeded! Just fire the callback
else
callback();
}
else
{
// No test specified. So, load necessary javascript/html/css and execute the callback
new ensureExecutor(data, callback, scope);
}
}
// ensureExecutor is the main class that does the job of ensure.
window.ensureExecutor = function(data, callback, scope)
{
this.data = this.clone(data);
this.callback = (typeof scope == "undefined" || null == scope ? callback : this.delegate(callback, scope));
this.loadStack = [];
if( data.js && data.js.constructor != Array ) this.data.js = [data.js];
if( data.html && data.html.constructor != Array ) this.data.html = [data.html];
if( data.css && data.css.constructor != Array ) this.data.css = [data.css];
if( typeof data.js == "undefined" ) this.data.js = [];
if( typeof data.html == "undefined" ) this.data.html = [];
if( typeof data.css == "undefined" ) this.data.css = [];
this.init();
this.load();
}
window.ensureExecutor.prototype = {
init : function()
{
// Fetch Javascript using Framework specific library
if( typeof jQuery != "undefined" )
{
this.getJS = HttpLibrary.loadJavascript_jQuery;
this.httpGet = HttpLibrary.httpGet_jQuery;
}
else if( typeof Prototype != "undefined" )
{
this.getJS = HttpLibrary.loadJavascript_Prototype;
this.httpGet = HttpLibrary.httpGet_Prototype;
}
else if( typeof Sys != "undefined" )
{
this.getJS = HttpLibrary.loadJavascript_MSAJAX;
this.httpGet = HttpLibrary.httpGet_MSAJAX;
}
else
{
throw "jQuery, Prototype or MS AJAX framework not found";
}
},
getJS : function(data)
{
// abstract function to get Javascript and execute it
},
httpGet : function(url, callback)
{
// abstract function to make HTTP GET call
},
load : function()
{
this.loadJavascripts( this.delegate( function() {
this.loadCSS( this.delegate( function() {
this.loadHtml( this.delegate( function() {
this.callback()
} ) )
} ) )
} ) );
},
loadJavascripts : function(complete)
{
var scriptsToLoad = this.data.js.length;
if( 0 === scriptsToLoad ) return complete();
this.forEach(this.data.js, function(href)
{
if( HttpLibrary.isUrlLoaded(href) || this.isTagLoaded('script', 'src', href) )
{
scriptsToLoad --;
}
else
{
this.getJS({
url: href,
success: this.delegate(function(content)
{
scriptsToLoad --;
HttpLibrary.registerUrl(href);
}),
error: this.delegate(function(msg)
{
scriptsToLoad --;
if(typeof this.data.error == "function") this.data.error(href, msg);
})
});
}
});
// wait until all the external scripts are downloaded
this.until({
test: function() { return scriptsToLoad === 0; },
delay: 50,
callback: this.delegate(function()
{
complete();
})
});
},
loadCSS : function(complete)
{
if( 0 === this.data.css.length ) return complete();
var head = HttpLibrary.getHead();
this.forEach(this.data.css, function(href)
{
if( HttpLibrary.isUrlLoaded(href) || this.isTagLoaded('link', 'href', href) )
{
// Do nothing
}
else
{
var self = this;
try
{
(function(href, head)
{
var link = document.createElement('link');
link.setAttribute("href", href);
link.setAttribute("rel", "Stylesheet");
link.setAttribute("type", "text/css");
head.appendChild(link);
HttpLibrary.registerUrl(href);
}).apply(window, [href, head]);
}
catch(e)
{
if(typeof self.data.error == "function") self.data.error(href, e.message);
}
}
});
complete();
},
loadHtml : function(complete)
{
var htmlToDownload = this.data.html.length;
if( 0 === htmlToDownload ) return complete();
this.forEach(this.data.html, function(href)
{
if( HttpLibrary.isUrlLoaded(href) )
{
htmlToDownload --;
}
else
{
this.httpGet({
url: href,
success: this.delegate(function(content)
{
htmlToDownload --;
HttpLibrary.registerUrl(href);
var parent = (this.data.parent || document.body.appendChild(document.createElement("p")));
if( typeof parent == "string" ) parent = document.getElementById(parent);
parent.innerHTML = content;
}),
error: this.delegate(function(msg)
{
htmlToDownload --;
if(typeof this.data.error == "function") this.data.error(href, msg);
})
});
}
});
// wait until all the external scripts are downloaded
this.until({
test: function() { return htmlToDownload === 0; },
delay: 50,
callback: this.delegate(function()
{
complete();
})
});
},
clone : function(obj)
{
var cloned = {};
for( var p in obj )
{
var x = obj[p];
if( typeof x == "object" )
{
if( x.constructor == Array )
{
var a = [];
for( var i = 0; i < x.length; i ++ ) a.push(x[i]);
cloned[p] = a;
}
else
{
cloned[p] = this.clone(x);
}
}
else
cloned[p] = x;
}
return cloned;
},
forEach : function(arr, callback)
{
var self = this;
for( var i = 0; i < arr.length; i ++ )
callback.apply(self, [arr[i]]);
},
delegate : function( func, obj )
{
var context = obj || this;
return function() { func.apply(context, arguments); }
},
until : function(o /* o = { test: function(){...}, delay:100, callback: function(){...} } */)
{
if( o.test() === true ) o.callback();
else window.setTimeout( this.delegate( function() { this.until(o); } ), o.delay || 50);
},
isTagLoaded : function(tagName, attName, value)
{
// Create a temporary tag to see what value browser eventually
// gives to the attribute after doing necessary encoding
var tag = document.createElement(tagName);
tag[attName] = value;
var tagFound = false;
var tags = document.getElementsByTagName(tagName);
this.forEach(tags, function(t)
{
if( tag[attName] === t[attName] ) { tagFound = true; return false }
});
return tagFound;
}
}
var userAgent = navigator.userAgent.toLowerCase();
// HttpLibrary is a cross browser, cross framework library to perform common operations
// like HTTP GET, injecting script into DOM, keeping track of loaded url etc. It provides
// implementations for various frameworks including jQuery, MSAJAX or Prototype
var HttpLibrary = {
browser : {
version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
safari: /webkit/.test( userAgent ),
opera: /opera/.test( userAgent ),
msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
},
loadedUrls : {},
isUrlLoaded : function(url)
{
return HttpLibrary.loadedUrls[url] === true;
},
unregisterUrl : function(url)
{
HttpLibrary.loadedUrls[url] = false;
},
registerUrl : function(url)
{
HttpLibrary.loadedUrls[url] = true;
},
createScriptTag : function(url, success, error)
{
var scriptTag = document.createElement("script");
scriptTag.setAttribute("type", "text/javascript");
scriptTag.setAttribute("src", url);
scriptTag.onload = scriptTag.onreadystatechange = function()
{
if ( (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
success();
}
};
scriptTag.onerror = function()
{
error(data.url + " failed to load");
};
var head = HttpLibrary.getHead();
head.appendChild(scriptTag);
},
getHead : function()
{
return document.getElementsByTagName("head")[0] || document.documentElement
},
globalEval : function(data)
{
var script = document.createElement("script");
script.type = "text/javascript";
if ( HttpLibrary.browser.msie )
script.text = data;
else
script.appendChild( document.createTextNode( data ) );
var head = HttpLibrary.getHead();
head.appendChild( script );
//head.removeChild( script );
},
loadJavascript_jQuery : function(data)
{
if( HttpLibrary.browser.safari )
{
return jQuery.ajax({
type: "GET",
url: data.url,
data: null,
success: function(content)
{
HttpLibrary.globalEval(content);
data.success();
},
error: function(xml, status, e)
{
if( xml && xml.responseText )
data.error(xml.responseText);
else
data.error(url +'\n' + e.message);
},
dataType: "html"
});
}
else
{
HttpLibrary.createScriptTag(data.url, data.success, data.error);
}
},
loadJavascript_MSAJAX : function(data)
{
if( HttpLibrary.browser.safari )
{
var params =
{
url: data.url,
success: function(content)
{
HttpLibrary.globalEval(content);
data.success(content);
},
error : data.error
};
HttpLibrary.httpGet_MSAJAX(params);
}
else
{
HttpLibrary.createScriptTag(data.url, data.success, data.error);
}
},
loadJavascript_Prototype : function(data)
{
if( HttpLibrary.browser.safari )
{
var params =
{
url: data.url,
success: function(content)
{
HttpLibrary.globalEval(content);
data.success(content);
},
error : data.error
};
HttpLibrary.httpGet_Prototype(params);
}
else
{
HttpLibrary.createScriptTag(data.url, data.success, data.error);
}
},
httpGet_jQuery : function(data)
{
return jQuery.ajax({
type: "GET",
url: data.url,
data: null,
success: data.success,
error: function(xml, status, e)
{
if( xml && xml.responseText )
data.error(xml.responseText);
else
data.error("Error occured while loading: " + url +'\n' + e.message);
},
dataType: data.type || "html"
});
},
httpGet_MSAJAX : function(data)
{
var _wRequest = new Sys.Net.WebRequest();
_wRequest.set_url(data.url);
_wRequest.set_httpVerb("GET");
_wRequest.add_completed(function (result)
{
var errorMsg = "Failed to load:" + data.url;
if (result.get_timedOut()) {
errorMsg = "Timed out";
}
if (result.get_aborted()) {
errorMsg = "Aborted";
}
if (result.get_responseAvailable()) data.success( result.get_responseData() );
else data.error( errorMsg );
});
var executor = new Sys.Net.XMLHttpExecutor();
_wRequest.set_executor(executor);
executor.executeRequest();
},
httpGet_Prototype : function(data)
{
new Ajax.Request(data.url, {
method: 'get',
evalJS: false, // Make sure prototype does not automatically evan scripts
onSuccess: function(transport, json)
{
data.success(transport.responseText || "");
},
onFailure : data.error
});
}
};
})();

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

Vue中处理组件的动态加载和切换Vue是一个流行的JavaScript框架,它提供了各种灵活的功能来处理组件的动态加载和切换。在本文中,我们将讨论一些Vue中处理组件动态加载和切换的方法,并提供具体的代码示例。动态加载组件是指根据需要在运行时动态加载组件。这样可以提高应用程序的性能和加载速度,因为只有当需要时才会加载相关的组件。Vue提供了async和awa

Python实现无头浏览器采集应用的页面动态加载与异步请求处理功能解析在网络爬虫中,有时候需要采集使用了动态加载或者异步请求的页面内容。传统的爬虫工具对于这类页面的处理存在一定的局限性,无法准确获取到页面上通过JavaScript生成的内容。而使用无头浏览器则可以解决这个问题。本文将介绍如何使用Python实现无头浏览器来采集使用动态加载与异步请求的页面内容

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

Golang热更新原理探究:动态加载与重载的奥秘引言:在软件开发领域,程序员们经常希望能够在不重启应用的情况下进行代码修改和更新。这样的需求对于开发效率和系统运行的可靠性都具有重要意义。而Golang作为一门现代化的编程语言,为开发者提供了许多便捷的机制来实现热更新。本文将深入探讨Golang热更新的原理,特别是动态加载和重载的奥秘,并将结合具体的代码示例进


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Linux新版
SublimeText3 Linux最新版

SublimeText3漢化版
中文版,非常好用

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)