`
yiminghe
  • 浏览: 1466724 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

赋值 script 到 innerHTMl 的Extjs 解决方案

阅读更多

 

老话题:

一般来说 innerHTML 只处理 html 即可,很方便代替冗余的dom操作,但是也有场合需要 从服务端返回一段代码+html一起付给innerHTML,
这种情况下script是并不执行的,有兴趣可以试试

 

<div id="scriptTest">
</div>
<script type="text/javascript">
window.onload=function(){
    document.getElementById("scriptTest").innerHTML="bbbb<script>alert(1);<"+"/"+"script>";
};
</script>

 

 

其原因就是:script解析与运行只在页面第一次加载过程中产生,页面加载完毕后,是不会执行script了。
而extjs 也考虑到了这个问题,比如:Ext.Element.update 不是简单的调用innerhtml ,它有 loadScripts 的参数配置,可以使得赋值给innerhtml的同时运行脚本。


Ext.Element.update (附注释):

 

update : function(html, loadScripts, callback){
         if(typeof html == "undefined"){
          html = "";
       }
        //如果指明不包含脚本直接执行哦
         if(loadScripts !== true){
             this.dom.innerHTML = html;
          if(typeof callback == "function"){
                callback();
           }
            return this;
       }
        var id = Ext.id();
        var dom = this.dom;

        html += '<span id="' + id + '"></span>';

               //定时判断id span是否已经解析完毕,span 解析完了,那么html也解析完了,span在他前面么
               //防止 html 脚本中引用到 html 中的元素
        E.onAvailable(id, function(){
           var hd = document.getElementsByTagName("head")[0];
           //脚本识别
            var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
            //外部脚本识别
            var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
          
            var typeRe = /\stype=([\'\"])(.*?)\1/i;

            var match;
            //html中包含有脚本,提炼出来单独执行
            while(match = re.exec(html)){
                var attrs = match[1];
                var srcMatch = attrs ? attrs.match(srcRe) : false;
                
                //外部脚本,在head中添加dom标签,动态载入脚本
                if(srcMatch && srcMatch[2]){
                   var s = document.createElement("script");
                   s.src = srcMatch[2];
                   var typeMatch = attrs.match(typeRe);
                   if(typeMatch && typeMatch[2]){
                       s.type = typeMatch[2];
                   }
                   hd.appendChild(s);
                
                // 内部脚本直接运行  
                }else if(match[2] && match[2].length > 0){
                    if(window.execScript) {
                       window.execScript(match[2]);
                    } else {
                       window.eval(match[2]);
                    }
                }
            }
            
            //删除检测html节点载入完毕指示元素
            var el = document.getElementById(id);
            if(el){Ext.removeNode(el);}
           if(typeof callback == "function"){
                callback();
            }
        });
        
        //只赋值html即可,脚本在html解析完毕后单独运行
        dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
        return this;
    },

 

 

分析:

代码要点:
1.利用正则表达式抽出脚本单独执行。
2.防止脚本中引用插入html的元素,利用轮训等待html解析完毕后再执行脚本。(在html最后加一个指示元素)

3.IE 使用 window.execScript 使得脚本在脱离当前闭包,在全局域内运行,firefox则使用 window.eval 来脱离当前闭包,直接使用 eval 会使得在当前闭包运行。

缺点:
Ext update 不能保证脚本执行顺序问题,不像浏览器解析外部脚本有严格顺序(block特性),动态载入 互相依赖脚本 的话顺序得不到保证,解决的话,得利用队列,并根据ie的 readystatechange与firefox的onload判断脚本载入完毕 ,手动保证脚本载入执行顺序,很麻烦。

 

改进:

 

由于 ext 中定时器要多次轮训判断制定 id 是否存在,其中的正则表达式其实可以缓存:

 

var RE_TAG = /<(\w+)/,
        RE_SIMPLE_TAG = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
        RE_SCRIPT = /<script([^>]*)>([\s\S]*?)<\/script>/ig,
        RE_SCRIPT_SRC = /\ssrc=(['"])(.*?)\1/i,
        RE_SCRIPT_CHARSET = /\scharset=(['"])(.*?)\1/i; 
function setHTML(elem, html, loadScripts, callback) {
        if (!loadScripts) {
            setHTMLSimple(elem, html);
            S.isFunction(callback) && callback();
            return;
        }

        var id = S.guid('ks-tmp-');
        html += '<span id="' + id + '"></span>';
        //see S.globalEval(text);
        //if text contains html() then will reset public shared RE_SCRIPT
        //so dupliacate our own
        var RE_SCRIPT_INNER = new RegExp(RE_SCRIPT);
        // 确保脚本执行时,相关联的 DOM 元素已经准备好
        S.available(id, function() {
            var hd = S.get('head'),
                match, attrs, srcMatch, charsetMatch,
                t, s, text;
            //share between intervals
            RE_SCRIPT_INNER.lastIndex = 0;
            while ((match = RE_SCRIPT_INNER.exec(html))) {
                attrs = match[1];
                srcMatch = attrs ? attrs.match(RE_SCRIPT_SRC) : false;

                // script via src
                if (srcMatch && srcMatch[2]) {
                    s = doc.createElement('script');
                    s.src = srcMatch[2];
                    // set charset
                    if ((charsetMatch = attrs.match(RE_SCRIPT_CHARSET)) && charsetMatch[2]) {
                        s.charset = charsetMatch[2];
                    }
                    s.async = true; // make sure async in gecko
                    hd.appendChild(s);
                }
                // inline script
                else if ((text = match[2]) && text.length > 0) {
                    S.globalEval(text);

                }
            }

            // 删除探测节点
            (t = doc.getElementById(id)) && DOM.remove(t);

            // 回调
            S.isFunction(callback) && callback();
        });

        setHTMLSimple(elem, html);
    }

 

不过需要注意的是:要复制RE_SCRIPT为RE_SCRIPT_INNER,在定时器间共享即可(每次设置 lastIndex ),不可全局共享,防止在动态执行的脚本中再调用html导致全局的RE_SCRIPT被重置!

 

 

 

PS: appendChild(elment) 与脚本


firefox 下当 elment 是通过 innerHTML 而包含 script 节点时,其内的脚本会执行:

 

// firefox 会执行
    div = doc.createElement('DIV');
    div.innerHTML = 'html 3<script>alert("set innerHTML via appendChild");<\/script>';
    t.appendChild(div);
 

 

附录:

 

innerHTML Property - MSDN

 

注意点:

1. innerHTML对于大部分 dom node 是可读可写的,但是也有例外 COL, COLGROUP, FRAMESET, HEAD, HTML, STYLE, TABLE, TBODY, TFOOT, THEAD, TITLE, TR 的innerhtml为只读。所以不能table.innerHTML= "<tr>....</tr>" 来插入行了,需要利用 div.innerHTML="<table><tr>...</tr></table>";得到 div.childNodes[0].childNodes  并插入到 table 中,以及对应 tr,thead 都有对应的嵌套标签包装,详见:KISSY.DOM.create


2.当使用 innerhtml 插入 html时,希望里面包含的 inline script 执行时,可以设置标签的defer 。(外联没用)。


3.当 html 只包含标签时,innerhtml不起作用:<script>为 NoScope元素,所以最好带个 scoped element ,例如文本节点或者input:

 

injectionDiv.innerHTML='<input type="hidden"/><script defer>alert("hello");</' + 'script>';
 

script - MDC


mozilla 的script机制 :一句话 defer 不保证,没用。

 

Have Your DOM and Script It Too


另外一种使得 html 与 script 打包运行的方法,非常巧妙。通过在原 html 后加一个 img 标签,在其 onload 内写代码即可,也省去了 extjs 例子中的 available 判断:

 

<p>This is the results of our Ajax call.</p>
 <img src="../images/loaded.gif" alt="" 
     onload="alert('Now that I have your attention...');this.parentNode.removeChild(this);" />

 


修改自测试用例 by lifesinger

分享到:
评论
1 楼 小蝶兒 2013-01-31  
感觉还是把脚本转到head里面去执行了,可是我想动态的加载script到div里面该怎么办

相关推荐

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

    解决 Ajax 返回 innerHTML 中 ...解决 Ajax 返回 innerHTML 中 JavaScript 不能运行问题的关键是将 JavaScript 代码从 Ajax 返回的数据中提取出来,并将其添加到 `&lt;head&gt;` 中,让浏览器能够正确地解析和执行。

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

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

    innerHtml(转)

    1. **innerHTML的基本用法**:如何通过`innerHTML`读取和修改元素的内容。例如,`element.innerHTML`返回元素的HTML,而`element.innerHTML = someHTMLString`则将元素内容替换为指定的HTML字符串。 2. **性能影响*...

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

    首先,我们要理解问题的核心:当一个`innerHTML`赋值操作包含`&lt;script&gt;`标签时,不同浏览器的处理方式不尽相同。在IE中,如果`&lt;script&gt;`标签带有`defer`属性,IE会等到整个HTML内容解析完毕后再执行这些脚本。然而,...

    layui 穿梭框transfer右侧数据赋值排序错乱小技巧.doc

    layui 穿梭框transfer右侧数据赋值排序错乱小技巧 ...本文提供了一个简单的解决方案,用于解决 layui 穿梭框transfer右侧数据赋值排序错乱的问题。该解决方案可以帮助开发者快速解决该问题,提高开发效率。

    innerHTML在IE中报错解决方案

    本篇文章将深入探讨innerHTML在IE中遇到的报错问题,以及如何找到解决方案。 首先,问题的出现是这样的:在使用innerHTML向table或者tr元素动态插入内容时,如果在IE9及以下版本,会遇到“Invalid target element ...

    ExtJs入门 ExtJs很好的电子书 ExtJS入门.pdf 入门ExtJs必备电子书

    - **DOM操作**是ExtJs中的一项基本技能,它涉及到对网页文档对象模型的修改。ExtJs提供了一套强大的API,使得DOM操作变得更加简单和直观。 - **示例**:在提供的部分代码中,我们可以看到如何使用`Ext.getDom()`...

    IE6-IE9中tbody的innerHTML不能赋值的解决方法

    在IE6到IE9这些较老的Internet Explorer浏览器中,存在一些与DOM操作相关的兼容...使用`innerHTML`为`&lt;tbody&gt;`元素赋值时,应先进行浏览器兼容性检测,并采用相应的解决方案,以确保在所有支持的浏览器中都能正常工作。

    ASPxGridView SelectionChanged服务端事件对其他组件赋值不起作用的解决办法

    在ASP.NET Web Forms开发中,DevExpress的ASPxGridView控件是一个功能强大的数据网格视图,常用于...这种解决方案充分利用了DevExpress的AJAX机制,实现了页面部分刷新,提高了用户体验,同时也解决了值丢失的问题。

    innerHTML的简单应用

    innerHTML的简单应用

    ExtJS增删改查

    ### ExtJS 增删改查详解 #### 一、ExtJS Grid的增删改查功能概述 在ExtJS框架中,Grid是一个非常关键的组件,主要用于展示表格数据,并支持数据的增删改查等基本操作。对于大多数Web应用程序而言,这种功能几乎是...

    innerHTML的使用

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

    IE6/7/8/9中Table/Select的innerHTML不能赋值的解决方法

    总的来说,当在IE6、7、8、9中遇到innerHTML无法赋值的问题时,开发者应转向使用DOM API进行更底层的操作,而不是依赖innerHTML属性。这可能需要更多的代码量,但能确保在所有浏览器中的兼容性。同时,随着现代...

    extjs中利用htmleditor与第三方打印插件Lodop结合使用WEB打印功能

    在EXTJS 3.4版本的应用中,集成第三方打印插件Lodop可以实现便捷的Web打印功能。Lodop是一款高效、易用的网页打印控件,它提供了丰富...总的来说,EXTJS结合Lodop的解决方案在Web打印领域具有较高的实用性和可扩展性。

    innerHTML 和 getElementsByName 在IE下面的bug 的解决

    针对上述提到的问题,文档中提供了一种解决方案,即通过自定义函数`GetElementsByName`来替代标准的getElementsByName方法。这个自定义函数不仅增加了对元素的检索,还通过遍历所有具有特定标签名的元素,并检查每个...

    IE6/IE7/IE8/IE9中tbody的innerHTML不能赋值的完美解决方案

    最近发现各大类库都能利用xxx[removed]=HTML片断来生成节点元素,再把它们插入到目标元素的各个位置上。这东西实际上就是insertAdjacentHTML,但是IE可恶的innerHTML把这优势变成劣势。

    react-Innerself使用innerHTML将ReactRedux转换成50行代码

    Innerself是一种轻量级的解决方案,通过innerHTML属性来实现React组件的渲染,以此达到精简代码的目的。在传统的React与Redux组合中,大量的代码用于状态管理和组件的生命周期处理,而Innerself的目标则是减少这些...

Global site tag (gtag.js) - Google Analytics