`
442136713
  • 浏览: 51874 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

让插入到innerHTML中的script跑起来

阅读更多

在做 ajax 编程时,我们常常需要将 xmlhttp 获取到的页面内容通过 innerHTML 来赋给某个容器(比如 div、span 或者 td 等),但是这里存在一个问题,就是我们将要赋给 innerHTML 的页面内容如果包含有脚本程序,这些脚本程序不管是外部脚本,还是内部脚本,可能(1)都不会被执行。这个问题在某些时候微不足道,甚至可以忽略,但有些时候,这个问题就非常严重,它很可能让我们的程序得不到预期的结果。因此我们需要解决这个问题。

如果你读过 MSDN,你会发现并非所有插入到 innerHTML 中的脚本都不能执行,如果这段脚本的 script 标签中包含了 defer 属性,IE 会正确的执行这些脚本程序。但不幸的是,Moziila/Firefox 和 Opera 可不吃这一套,不管 script 标签有没有设置 defer 属性,这些浏览器都不会向 IE 那样去执行插入到 innerHTML 中的脚本。

但不管脚本是否被执行了,有一点我们可以肯定,那就是这些脚本确实被插入到了 innerHTML 中,如果不相信,你可以 alert 一下看看。但如果你真的 alert 了,你也可能会发现有一种例外情况存在,那就是如果脚本在 innerHTML 内容开头的话,那么 IE 浏览器将会忽略掉这段脚本,而 Moziila/Firefox 和 Opera 却不会。

好了,问题分析的差不多了,我们来看看如何解决吧。

解决的思路其实很简单,那就是将插入到 innerHTML 中的所有脚本取出来,然后一一执行。不过我们先要解决上面的两个问题。

先来看第一个问题,如何避免在 IE 中重复执行 innerHTML 中带有 defer 属性的脚本。这个很容易,只需要先确定浏览器是否是 IE,然后再检测将要执行的脚本是否带有 defer 属性即可。需要注意的是,在判断 IE 浏览器时,我们需要避免被 opera 的浏览器识别欺骗。这一点我们在后面的代码中将会看到它是如何做的。

接下来,看 IE 忽略 innerHTML 开头脚本的问题,这个也很容易解决。只需要在要插入到 innerHTML 中的内容的开头附加一段不是脚本的内容,就可以了。但不要试图附加一个空内容的标签,或者空格、回车、换行等,这将不起作用,开头的脚本仍然会被忽略。也不要试图附加  ,虽然这可以让开头的脚本不再被忽略,但这个   仍然会影响原有内容的显示,虽然你可能觉得不明显,但是对于挑剔的用户来说,这可能是无法容忍的。因此,为了让附加的内容既可以起到避免开头脚本被忽略的功能,又不会造成不良影响,我们将附加这么一段内容:

<span style="display: none">hack ie</span>
虽然上面这段内容有一定的长度,但是它并不会显示,而且这个插入的标签没有 id 也没有 name,所以也不会跟原来内容中的某些标签的 id 或者 name 产生冲突。不过这里有一点要注意,这里也要判断是否是 IE,然后再决定加不加这段内容,因为其他某些浏览器可能不支持 display: none 这个 CSS 修饰(例如 Opera Mini),如果加上这段代码会影响最终的显示效果。

下面我们来看看如何取出脚本并执行。

取出脚本很容易,只需要用 innerHTML 所在对象的 getElementsByTagName 方法就可以了,这个方法对几乎所有的容器标签都管用。取出脚本以后,我们要一一判断它们是外部脚本还是内部脚本。

先来看外部脚本,如果是外部脚本,我们选择了这样一种方法,即先创建这个外部脚本的一个副本对象,并设置它的 defer 属性为 true(这一点是为了让 IE 浏览器能正确执行),然后用 appendChild 方法将这个副本对象插入到 head 中。这里你可能会问,为什么不是插入到 innerHTML 所在的对象中呢?插入到 innerHTML 所在的对象中不是更好吗?如果你试一下就会知道,如果插入到 innerHTML 所在的对象中,在 IE 浏览器中没有问题,但是在 Mozilla/Firefox 和 Opera 浏览器中会有一些问题。问题是如果在 Firefox 上这样做,浏览器会停止响应(这是在 Firefox 1.5 上的测试结果,其他版本是否有此问题,尚不得知),而在 Opera 上,脚本会莫名其妙的执行两次(这是在 Opera 8.5 上的测试结果,其它版本的 Opera 是否由此问题,也尚不得知)。为了避免这些问题,所以我选择了插入到 head 中。

再来看内部脚本,内部脚本的内容我们可以直接用脚本对象的 text 属性来获取,这里我们使用脚本对象的 text 属性而不是 innerHTML 属性,是因为在 Opera 浏览器中,脚本对象的 innerHTML 属性是空的,只有用 text 属性才能获取到脚本内容。执行内部脚本直接用 eval 即可。但是脚本可能会被包含在 HTML 的注释标签中,因此我们需要先将注释标签去掉,不然在 IE 中会出错。

上面的分析看上去很完美了,但是实际上还是有问题,一个是 document.write 和 document.writeln 的问题,这个问题在 Blueidea 上,bound0 给出了一个思路,就是替换掉默认的 document.write 和 document.writeln 方法,不过他用的是字符串替换,因此只对内部脚本有效,对外部脚本就没办法了,因此我想了个更通用的办法,就是直接把 document.write 和 document.writeln 重新定义,这样不管内部脚本还是外部脚本执行的就都是我们我们自己定义的 document.write 和 document.writeln 了。不过也有副作用,就是这两个函数在当前页面中就不能再像原来一样使用了,不过这两个函数在页面加载完之后一般是不会再用到了,因此这里重新定义它们所带来的副作用影响很小。但是还有个问题是,尽管这样,我们仍然无法保证 document.write 或 document.writeln 输出的内容会显示在最合适的位置,它只是把内容附加到了我们放置内容的容器中。

另一个问题是 eval 引起的问题,一个是 Blueidea 上的 hutia 说的作用域的问题,另一个问题是如果用 eval 执行的内部脚本的话,内部脚本会在外部脚本加载完之前就开始执行了。要解决这个两个问题可以采用 window.setTimeout 这个函数,让每个脚本都延时一段后再执行,外部脚本延时时间可以设的较长,以保证其能够完全加载,而内部脚本则可以设置为很短,因为一个脚本执行的时间通常是很短的,这样既可以保证不会改变作用域,又可以基本保证脚本执行顺序不会改变了(这种方法对于保证执行顺序上也不一定会 100% 有效,如果网络非常繁忙,外部脚本可能在设置的时间内加载不完,但至少比直接用 eval 的时候好多了)。

如果按照前面的方式实现,对于大多数脚本来说可以正常执行了。但是如果 script 中带有 defer 属性,IE 会自己运行那段代码(前面提过了),因此它会打乱执行的顺序。另外 document.write 和 document.writeln 写入的代码都回被添加到最后面,而不是脚本所在的位置上,因此这也是个问题。

为了解决这两个问题,我们需要对前面的解决方法作一些改变。首先我们不能先把内容赋值给 innerHTML,然后再通过它取脚本了,我们需要直接对内容分析来取出脚本。另外,脚本以外的 HTML 部分也不能直接赋值给 innerHTML,需要在脚本执行以后,将原有的 HTML 内容和 document.write\writeln 写的内容按照顺序合并到一起再赋值给 innerHTML,这里要注意,我们不能一部分一部分的将这些内容连接到 innerHTML 后面,因为其中可能有半个标签的内容,这种情况下,浏览器很容易发生错误。而且你会看到页面反复刷新的情况出现。而如果先放入缓冲区,最后一次赋给 innerHTML,就不会出现这种问题了。

另外放入缓冲区的好处是,当脚本执行完后,可以检查缓冲区中是否还有新的脚本,如果有,再递归执行,这样就可以解决 document.write 和 document.writeln 写的脚本也可以执行的问题了。

2006-6-4 更新:

修正了插入到 innerHTML 中的脚本无法获取插入到 innerHTML 中对象的问题。(感谢网友 DE 的提醒)。

增加了对同一容器中内容设置的共享锁,使得连续设置同一个容器内的时,不会再发生冲突。(感谢新加坡网友 Jason Li 的提醒)。

2006-5-29 更新:

增加了使用外部脚本缓存功能,提高了第二次加载相同外部脚本的速度。

2006-5-23 更新:

在热心的使用者 johnZEN 的提醒下,增加了共享锁,使得同时设置多个容器内的内容时,不会再发生冲突。

在网友 udbjatwfn 的提醒下,修正了 IE 中存在的内部脚本执行作用域错误的问题。

下面是最后本人的实现代码:

JavaScript代码
 
/* 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);    
}   


出处:http://blog.csdn.net/lee576/archive/2008/11/20/3341146.aspx

 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lee576/archive/2008/11/20/3341146.aspx

分享到:
评论

相关推荐

    让插入到 innerHTML 中的 script 跑起来的代码第1/2页

    总结起来,要使插入到`innerHTML`的`&lt;script&gt;`脚本在所有主流浏览器中都能正确执行,我们需要: 1. 检查并处理`defer`属性,确保脚本在适当的时候执行。 2. 分别处理外部脚本和内部脚本,确保它们被正确地创建和...

    关于在innerHTML中JS不执行的问题

    例如,如果你有`&lt;script&gt;`标签,可以创建一个新的`script`元素,然后设置其`src`属性,再将其添加到目标元素的子节点中。 ```javascript let script = document.createElement('script'); script.src = 'your_...

    解决ajax返回innerHTML中javascript不能运行问题

    在上面的代码中,我们使用正则表达式来匹配 Ajax 返回的数据中是否有 `&lt;script&gt;` 标签,如果存在则取出标签内部的内容,并创建新的 `&lt;script&gt;` 标签将其添加到 `&lt;head&gt;` 中,让浏览器解析。 解决 Ajax 返回 ...

    让innerHTML中的script脚本执行起来

    下面介绍怎么样让跑起来。 分析问题 既然只在文档加载中才被解析,那么我就得调用[removed]()方法来重新加载一次,但是这样的话原来页面的内容也会被覆盖掉了,所以不得不用IFRAME把[removed]()装载起来。 解决问题 ...

    innerHTML的简单应用

    innerHTML的简单应用

    innerHtml(转)

    标题中的"innerHtml(转)"可能是指HTML DOM(Document Object Model)中的一个属性`innerHTML`,这是一个非常关键的概念,特别是在JavaScript中用于处理HTML元素的内容。`innerHTML`允许我们获取或设置HTML元素内部...

    js中innerHTML与innerText的用法与区别

    ### JavaScript中的innerHTML与innerText的用法与区别 在前端开发中,经常需要用到JavaScript来操作DOM元素,其中`innerHTML`和`innerText`是非常重要的属性,它们分别用于获取或设置元素内部的HTML内容和纯文本...

    Vue中插入HTML代码的方法

    我们需要吧&lt;p&gt;Hello World插入到&lt;p&gt;My name is Pjee应该如何做? 一、使用v-html v-html:更新元素的 innerHTML const text = `&lt;p&gt;Hello World&lt;/&gt;` My name is Pjee &lt;p v-html='text'&gt;&lt;/p&gt; 注意:你的站点...

    让innerHTML的脚本也可以运行起来

    对于非IE浏览器,它将元素从DOM中移除,设置新的`innerHTML`,然后将其重新插入到之前的位置,这样Firefox就会执行新的脚本。 需要注意的是,这种方法可能不适用于所有情况,特别是在复杂的DOM操作或动态加载的脚本...

    Script中defer的作用

    document.body.innerHTML += '这是由script1.js动态插入的段落。&lt;/p&gt;'; ``` 而`script2.js`包含: ```javascript document.body.innerHTML += '这是由script2.js动态插入的段落。&lt;/p&gt;'; ``` 当页面加载完成并执行...

    innerHTML和innerText的区别

    在 JavaScript 中,innerHTML 和 innerText 是两个常用的属性,它们用于获取 HTML 元素中的内容。虽然它们都是用于获取内容的,但它们在获取...因此,在实际开发中,尽量使用 innerHTML 属性,以确保浏览器的兼容性。

    简单日历和innerHTML使用

    简单日历和innerHTML使用 javascript的简单应用实例 可以自定义Css

    基于innerHTML中的script广告实现代码[广告全部放在一个js里面] <font color=red>原创</font>

    其次,由于安全和性能考虑,一些浏览器可能会阻止或延迟通过`innerHTML`插入的`script`标签的执行,这可能导致广告无法正常显示或功能受限。 在提供的代码示例中,作者展示了如何使用`innerHTML`来插入不同类型的...

    Angular中innerHTML标签的样式不起作用的原因解析

    在这个框架中,出于安全考虑,Angular 默认对所有通过数据绑定插入到 DOM 中的内容进行清理和转义,防止潜在的跨站脚本攻击(XSS)。这种行为影响了开发者使用 HTML 属性如 `innerHTML` 来动态渲染内容时的表现,...

    innerHTML的使用

    innerHTML的使用document.getElementById("id").innerHTML = "contenttext";

    利用innerHTML实现隐显效果-两种实现方法

    在网页开发中,innerHTML属性是JavaScript中用于读取或设置HTML元素内HTML内容的重要工具。本文将探讨如何利用innerHTML实现页面元素的隐藏与显示效果,同时提供两种不同的实现方法。 首先,我们需要理解innerHTML...

    下拉框的内容通过js插入到table中

    下面将详细阐述如何通过JavaScript的`insertRow`方法将下拉框的内容插入到表格中,并使用`delRow`方法删除表格中的行。 首先,我们需要了解HTML的`&lt;select&gt;`元素,它是创建下拉框的主要方式。下拉框通常包含一系列`...

    python的xpath获取div标签内html内容,实现innerhtml功能的方法

    python的xpath没有获取div标签内html内容的功能,也就是获取div或a标签中的innerhtml,写了个小程序实现一下: 源代码 [webadmin@centos7 csdnd4q] #162&gt; vim /mywork/python/csdnd4q/z040.py #去掉最外层标签,...

Global site tag (gtag.js) - Google Analytics