国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

讓插入到 innerHTML 中的 script 跑起來的實(shí)現(xiàn)代碼

2019-11-21 02:34:49
字體:
供稿:網(wǎng)友

這個(gè)問題在某些時(shí)候微不足道,甚至可以忽略,但有些時(shí)候,這個(gè)問題就非常嚴(yán)重,它很可能讓我們的程序得不到預(yù)期的結(jié)果。因此我們需要解決這個(gè)問題。

如果你讀過 MSDN,你會(huì)發(fā)現(xiàn)并非所有插入到 innerHTML 中的腳本都不能執(zhí)行,如果這段腳本的 script 標(biāo)簽中包含了 defer 屬性,IE 會(huì)正確的執(zhí)行這些腳本程序。但不幸的是,Moziila/Firefox 和 Opera 可不吃這一套,不管 script 標(biāo)簽有沒有設(shè)置 defer 屬性,這些瀏覽器都不會(huì)向 IE 那樣去執(zhí)行插入到 innerHTML 中的腳本。

但不管腳本是否被執(zhí)行了,有一點(diǎn)我們可以肯定,那就是這些腳本確實(shí)被插入到了 innerHTML 中,如果不相信,你可以 alert 一下看看。但如果你真的 alert 了,你也可能會(huì)發(fā)現(xiàn)有一種例外情況存在,那就是如果腳本在 innerHTML 內(nèi)容開頭的話,那么 IE 瀏覽器將會(huì)忽略掉這段腳本,而 Moziila/Firefox 和 Opera 卻不會(huì)。

好了,問題分析的差不多了,我們來看看如何解決吧。

解決的思路其實(shí)很簡(jiǎn)單,那就是將插入到 innerHTML 中的所有腳本取出來,然后一一執(zhí)行。不過我們先要解決上面的兩個(gè)問題。

先來看第一個(gè)問題,如何避免在 IE 中重復(fù)執(zhí)行 innerHTML 中帶有 defer 屬性的腳本。這個(gè)很容易,只需要先確定瀏覽器是否是 IE,然后再檢測(cè)將要執(zhí)行的腳本是否帶有 defer 屬性即可。需要注意的是,在判斷 IE 瀏覽器時(shí),我們需要避免被 opera 的瀏覽器識(shí)別欺騙。這一點(diǎn)我們?cè)诤竺娴拇a中將會(huì)看到它是如何做的。

接下來,看 IE 忽略 innerHTML 開頭腳本的問題,這個(gè)也很容易解決。只需要在要插入到 innerHTML 中的內(nèi)容的開頭附加一段不是腳本的內(nèi)容,就可以了。但不要試圖附加一個(gè)空內(nèi)容的標(biāo)簽,或者空格、回車、換行等,這將不起作用,開頭的腳本仍然會(huì)被忽略。也不要試圖附加  ,雖然這可以讓開頭的腳本不再被忽略,但這個(gè)   仍然會(huì)影響原有內(nèi)容的顯示,雖然你可能覺得不明顯,但是對(duì)于挑剔的用戶來說,這可能是無法容忍的。因此,為了讓附加的內(nèi)容既可以起到避免開頭腳本被忽略的功能,又不會(huì)造成不良影響,我們將附加這么一段內(nèi)容:

復(fù)制代碼 代碼如下:

<span style="display: none">hack ie</span>

雖然上面這段內(nèi)容有一定的長(zhǎng)度,但是它并不會(huì)顯示,而且這個(gè)插入的標(biāo)簽沒有 id 也沒有 name,所以也不會(huì)跟原來內(nèi)容中的某些標(biāo)簽的 id 或者 name 產(chǎn)生沖突。不過這里有一點(diǎn)要注意,這里也要判斷是否是 IE,然后再?zèng)Q定加不加這段內(nèi)容,因?yàn)槠渌承g覽器可能不支持 display: none 這個(gè) CSS 修飾(例如 Opera Mini),如果加上這段代碼會(huì)影響最終的顯示效果。

下面我們來看看如何取出腳本并執(zhí)行。

取出腳本很容易,只需要用 innerHTML 所在對(duì)象的 getElementsByTagName 方法就可以了,這個(gè)方法對(duì)幾乎所有的容器標(biāo)簽都管用。取出腳本以后,我們要一一判斷它們是外部腳本還是內(nèi)部腳本。

先來看外部腳本,如果是外部腳本,我們選擇了這樣一種方法,即先創(chuàng)建這個(gè)外部腳本的一個(gè)副本對(duì)象,并設(shè)置它的 defer 屬性為 true(這一點(diǎn)是為了讓 IE 瀏覽器能正確執(zhí)行),然后用 appendChild 方法將這個(gè)副本對(duì)象插入到 head 中。這里你可能會(huì)問,為什么不是插入到 innerHTML 所在的對(duì)象中呢?插入到 innerHTML 所在的對(duì)象中不是更好嗎?如果你試一下就會(huì)知道,如果插入到 innerHTML 所在的對(duì)象中,在 IE 瀏覽器中沒有問題,但是在 Mozilla/Firefox 和 Opera 瀏覽器中會(huì)有一些問題。問題是如果在 Firefox 上這樣做,瀏覽器會(huì)停止響應(yīng)(這是在 Firefox 1.5 上的測(cè)試結(jié)果,其他版本是否有此問題,尚不得知),而在 Opera 上,腳本會(huì)莫名其妙的執(zhí)行兩次(這是在 Opera 8.5 上的測(cè)試結(jié)果,其它版本的 Opera 是否由此問題,也尚不得知)。為了避免這些問題,所以我選擇了插入到 head 中。

再來看內(nèi)部腳本,內(nèi)部腳本的內(nèi)容我們可以直接用腳本對(duì)象的 text 屬性來獲取,這里我們使用腳本對(duì)象的 text 屬性而不是 innerHTML 屬性,是因?yàn)樵?Opera 瀏覽器中,腳本對(duì)象的 innerHTML 屬性是空的,只有用 text 屬性才能獲取到腳本內(nèi)容。執(zhí)行內(nèi)部腳本直接用 eval 即可。但是腳本可能會(huì)被包含在 HTML 的注釋標(biāo)簽中,因此我們需要先將注釋標(biāo)簽去掉,不然在 IE 中會(huì)出錯(cuò)。

上面的分析看上去很完美了,但是實(shí)際上還是有問題,一個(gè)是 document.write 和 document.writeln 的問題,這個(gè)問題在 Blueidea 上,bound0 給出了一個(gè)思路,就是替換掉默認(rèn)的 document.write 和 document.writeln 方法,不過他用的是字符串替換,因此只對(duì)內(nèi)部腳本有效,對(duì)外部腳本就沒辦法了,因此我想了個(gè)更通用的辦法,就是直接把 document.write 和 document.writeln 重新定義,這樣不管內(nèi)部腳本還是外部腳本執(zhí)行的就都是我們我們自己定義的 document.write 和 document.writeln 了。不過也有副作用,就是這兩個(gè)函數(shù)在當(dāng)前頁面中就不能再像原來一樣使用了,不過這兩個(gè)函數(shù)在頁面加載完之后一般是不會(huì)再用到了,因此這里重新定義它們所帶來的副作用影響很小。但是還有個(gè)問題是,盡管這樣,我們?nèi)匀粺o法保證 document.write 或 document.writeln 輸出的內(nèi)容會(huì)顯示在最合適的位置,它只是把內(nèi)容附加到了我們放置內(nèi)容的容器中。

另一個(gè)問題是 eval 引起的問題,一個(gè)是 Blueidea 上的 hutia 說的作用域的問題,另一個(gè)問題是如果用 eval 執(zhí)行的內(nèi)部腳本的話,內(nèi)部腳本會(huì)在外部腳本加載完之前就開始執(zhí)行了。要解決這個(gè)兩個(gè)問題可以采用 window.setTimeout 這個(gè)函數(shù),讓每個(gè)腳本都延時(shí)一段后再執(zhí)行,外部腳本延時(shí)時(shí)間可以設(shè)的較長(zhǎng),以保證其能夠完全加載,而內(nèi)部腳本則可以設(shè)置為很短,因?yàn)橐粋€(gè)腳本執(zhí)行的時(shí)間通常是很短的,這樣既可以保證不會(huì)改變作用域,又可以基本保證腳本執(zhí)行順序不會(huì)改變了(這種方法對(duì)于保證執(zhí)行順序上也不一定會(huì) 100% 有效,如果網(wǎng)絡(luò)非常繁忙,外部腳本可能在設(shè)置的時(shí)間內(nèi)加載不完,但至少比直接用 eval 的時(shí)候好多了)。

如果按照前面的方式實(shí)現(xiàn),對(duì)于大多數(shù)腳本來說可以正常執(zhí)行了。但是如果 script 中帶有 defer 屬性,IE 會(huì)自己運(yùn)行那段代碼(前面提過了),因此它會(huì)打亂執(zhí)行的順序。另外 document.write 和 document.writeln 寫入的代碼都回被添加到最后面,而不是腳本所在的位置上,因此這也是個(gè)問題。

為了解決這兩個(gè)問題,我們需要對(duì)前面的解決方法作一些改變。首先我們不能先把內(nèi)容賦值給 innerHTML,然后再通過它取腳本了,我們需要直接對(duì)內(nèi)容分析來取出腳本。另外,腳本以外的 HTML 部分也不能直接賦值給 innerHTML,需要在腳本執(zhí)行以后,將原有的 HTML 內(nèi)容和 document.write/writeln 寫的內(nèi)容按照順序合并到一起再賦值給 innerHTML,這里要注意,我們不能一部分一部分的將這些內(nèi)容連接到 innerHTML 后面,因?yàn)槠渲锌赡苡邪雮€(gè)標(biāo)簽的內(nèi)容,這種情況下,瀏覽器很容易發(fā)生錯(cuò)誤。而且你會(huì)看到頁面反復(fù)刷新的情況出現(xiàn)。而如果先放入緩沖區(qū),最后一次賦給 innerHTML,就不會(huì)出現(xiàn)這種問題了。

另外放入緩沖區(qū)的好處是,當(dāng)腳本執(zhí)行完后,可以檢查緩沖區(qū)中是否還有新的腳本,如果有,再遞歸執(zhí)行,這樣就可以解決 document.write 和 document.writeln 寫的腳本也可以執(zhí)行的問題了。

2006-6-4 更新:

修正了插入到 innerHTML 中的腳本無法獲取插入到 innerHTML 中對(duì)象的問題。(感謝網(wǎng)友 DE 的提醒)。

增加了對(duì)同一容器中內(nèi)容設(shè)置的共享鎖,使得連續(xù)設(shè)置同一個(gè)容器內(nèi)的時(shí),不會(huì)再發(fā)生沖突。(感謝新加坡網(wǎng)友 Jason Li 的提醒)。

2006-5-29 更新:

增加了使用外部腳本緩存功能,提高了第二次加載相同外部腳本的速度。

2006-5-23 更新:

在熱心的使用者 johnZEN 的提醒下,增加了共享鎖,使得同時(shí)設(shè)置多個(gè)容器內(nèi)的內(nèi)容時(shí),不會(huì)再發(fā)生沖突。

在網(wǎng)友 udbjatwfn 的提醒下,修正了 IE 中存在的內(nèi)部腳本執(zhí)行作用域錯(cuò)誤的問題。

下面是最后本人的實(shí)現(xiàn)代碼:
復(fù)制代碼 代碼如下:

/* innerhtml.js
* Copyright Ma Bingyao <andot@ujn.edu.cn>
* Version: 1.9
* LastModified: 2006-06-04
* This library is free. You can redistribute it and/or modify it.
* http://www.coolcode.cn/?p=117
*/

var global_html_pool = [];
var global_script_pool = [];
var global_script_src_pool = [];
var global_lock_pool = [];
var innerhtml_lock = null;
var document_buffer = "";

function set_innerHTML(obj_id, html, time) {
if (innerhtml_lock == null) {
innerhtml_lock = obj_id;
}
else if (typeof(time) == "undefined") {
global_lock_pool[obj_id + "_html"] = html;
window.setTimeout("set_innerHTML('" + obj_id + "', global_lock_pool['" + obj_id + "_html']);", 10);
return;
}
else if (innerhtml_lock != obj_id) {
global_lock_pool[obj_id + "_html"] = html;
window.setTimeout("set_innerHTML('" + obj_id + "', global_lock_pool['" + obj_id + "_html'], " + time + ");", 10);
return;
}

function get_script_id() {
return "script_" + (new Date()).getTime().toString(36)
+ Math.floor(Math.random() * 100000000).toString(36);
}

document_buffer = "";

document.write = function (str) {
document_buffer += str;
}
document.writeln = function (str) {
document_buffer += str + "/n";
}

global_html_pool = [];

var scripts = [];
html = html.split(/<//script>/i);
for (var i = 0; i < html.length; i++) {
global_html_pool[i] = html[i].replace(/<script[/s/S]*$/ig, "");
scripts[i] = {text: '', src: '' };
scripts[i].text = html[i].substr(global_html_pool[i].length);
scripts[i].src = scripts[i].text.substr(0, scripts[i].text.indexOf('>') + 1);
scripts[i].src = scripts[i].src.match(/src/s*=/s*(/"([^/"]*)/"|/'([^/']*)/'|([^/s]*)[/s>])/i);
if (scripts[i].src) {
if (scripts[i].src[2]) {
scripts[i].src = scripts[i].src[2];
}
else if (scripts[i].src[3]) {
scripts[i].src = scripts[i].src[3];
}
else if (scripts[i].src[4]) {
scripts[i].src = scripts[i].src[4];
}
else {
scripts[i].src = "";
}
scripts[i].text = "";
}
else {
scripts[i].src = "";
scripts[i].text = scripts[i].text.substr(scripts[i].text.indexOf('>') + 1);
scripts[i].text = scripts[i].text.replace(/^/s*</!--/s*/g, "");
}
}

var s;
if (typeof(time) == "undefined") {
s = 0;
}
else {
s = time;
}

var script, add_script, remove_script;

for (var i = 0; i < scripts.length; i++) {
var add_html = "document_buffer += global_html_pool[" + i + "];/n";
add_html += "document.getElementById('" + obj_id + "').innerHTML = document_buffer;/n";
script = document.createElement("script");
if (scripts[i].src) {
script.src = scripts[i].src;
if (typeof(global_script_src_pool[script.src]) == "undefined") {
global_script_src_pool[script.src] = true;
s += 2000;
}
else {
s += 10;
}
}
else {
script.text = scripts[i].text;
s += 10;
}
script.defer = true;
script.type = "text/javascript";
script.id = get_script_id();
global_script_pool[script.id] = script;
add_script = add_html;
add_script += "document.getElementsByTagName('head').item(0)";
add_script += ".appendChild(global_script_pool['" + script.id + "']);/n";
window.setTimeout(add_script, s);
remove_script = "document.getElementsByTagName('head').item(0)";
remove_script += ".removeChild(document.getElementById('" + script.id + "'));/n";
remove_script += "delete global_script_pool['" + script.id + "'];/n";
window.setTimeout(remove_script, s + 10000);
}

var end_script = "if (document_buffer.match(/<///script>/i)) {/n";
end_script += "set_innerHTML('" + obj_id + "', document_buffer, " + s + ");/n";
end_script += "}/n";
end_script += "else {/n";
end_script += "document.getElementById('" + obj_id + "').innerHTML = document_buffer;/n";
end_script += "innerhtml_lock = null;/n";
end_script += "}";
window.setTimeout(end_script, s);
}

JS調(diào)用方法:
JavaScript代碼
復(fù)制代碼 代碼如下:

set_innerHTML('要插入innerhtml的ID名稱', '要插入的代碼');

方案2:來自ajaxwing的innerHTML簡(jiǎn)單版
不過這個(gè)實(shí)現(xiàn)有一點(diǎn)問題就是腳本中的 document.write 和 document.writeln 縮寫的內(nèi)容位置是不對(duì)的。

調(diào)用方法:
JavaScript代碼
setInnerHTML('DOM 樹中的節(jié)點(diǎn)', '要插入的代碼');
JavaScript代碼
復(fù)制代碼 代碼如下:

/*
* 描述:跨瀏覽器的設(shè)置 innerHTML 方法
* 允許插入的 HTML 代碼中包含 script 和 style
* 作者:kenxu <ken@ajaxwing.com>
* 日期:2006-03-23
* 參數(shù):
* el: 合法的 DOM 樹中的節(jié)點(diǎn)
* htmlCode: 合法的 HTML 代碼
* 經(jīng)測(cè)試的瀏覽器:ie5+, firefox1.5+, opera8.5+
*/
var setInnerHTML = function (el, htmlCode) {
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf('msie') >= 0 && ua.indexOf('opera') < 0) {
htmlCode = '<div style="display:none">for IE</div>' + htmlCode;
htmlCode = htmlCode.replace(/<script([^>]*)>/gi,
'<script$1 defer>');
el.innerHTML = htmlCode;
el.removeChild(el.firstChild);
} else {
var el_next = el.nextSibling;
var el_parent = el.parentNode;
el_parent.removeChild(el);
el.innerHTML = htmlCode;
if (el_next) {
el_parent.insertBefore(el, el_next)
} else {
el_parent.appendChild(el);
}
}
}

基于原作者的不讓轉(zhuǎn)載,導(dǎo)致所有的測(cè)試代碼都沒有了,本來應(yīng)該留一份的。唉
不過武林網(wǎng)特為大家制作了一個(gè)例子,以后大家可以全站的使用js控制廣告,減少連接數(shù)。
//m.survivalescaperooms.com/article/20068.htm

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 隆安县| 永州市| 仁寿县| 江华| 连州市| 论坛| 香格里拉县| 梁平县| 永登县| 五寨县| 永川市| 夹江县| 拉孜县| 通辽市| 潍坊市| 望城县| 桑植县| 云龙县| 建水县| 开江县| 府谷县| 崇义县| 湖州市| 望谟县| 沂水县| 桐庐县| 邵阳县| 怀仁县| 罗平县| 泾阳县| 浦江县| 金溪县| 镇平县| 黑河市| 莱阳市| 昌平区| 雅江县| 苗栗县| 晴隆县| 离岛区| 伊川县|