今天来简单聊聊如何让 innerHTML 进来的 scrip 代码跑起来的问题。

前台请求一个接口,接口返回一些 HTML 标签拼接成的字符串,以供前端直接 innerHTML 生成 DOM 元素,这样的做法非常普遍。但是你是否遇到过,如果字符串中拼接的 HTML 标签中有 script 标签,那么该段脚本是无法执行的,这并不是 bug,而是 w3c 的文档规定的。

比如如下这段代码,innerHTML 插入的脚本(alert)并不会执行:

<div id="myDiv">
</div>

<script>
  // 模拟接口返回数据
  var strVar = "";
    strVar += "<script>alert('hello world')<\/script>";

  document.getElementById('myDiv').innerHTML = strVar;
</script>

那么问题来了,如何使得 innerHTML 进来的 script 代码能够跑起来?

方案一:重新构造 script 标签

思路非常简单,构造新的 script 标签,然后该标签的 innerHTML 赋值为需要渲染的脚本。伪代码如下:

var s = document.createElement('script');
s.innerHTML = text;
document.body.appendChild(s);

当然如果要写的完美一点,执行完 s 后还需要 remove 掉。如何获取 text 值?两个方法,其一可以正则匹配 strVal,提取 script 标签内的内容,这点和 BigRender 类似,而 BigRender 除了提取 script 标签,还需要提取 style 标签,无疑更复杂;第二个方法是可以用 document.getElementsByTagName('script') 获取插入 DOM 但并未执行的 script 脚本,但是这样会把页面所有脚本全部提取,所以还需要判断。(可以获取某一节点下的 script 标签)

如果是外部的 js 文件,需要为新建的 script 元素添加 src 属性即可。

方案二:eval 大法

事实上,如果是 inline JavaScript,方案一求得的 text,可以直接 eval 之。

但是如果是外联 js 文件,同上,需要新建 script 标签然后指定 src 属性。

方案三:document.write

document.write 接收一个字符串作为参数,并且支持 script 标签以及其他 HTML 标签拼成的字符串,看起来似乎是完美的方案。不过鉴于 document.write 的怪属性,只适合 strVal 在首页输出流中的渲染情况,如果是异步的请求,就可以放弃了。

举个栗子:

hello world
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<div>
<script>
    $.get('data.php', function(data) {
      document.write(data);
    })
</script>
</div>

目的似乎是为了在 div 中嵌入 data,但是 hello world 字样也消失了,很显然,异步请求后重启输入流,将页面全部覆盖掉了。

方案四:jQuery html() 方法

使用封装过的方法无疑是个好办法:

<div id="myDiv"></div>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
  $.get('data.php', function(data) {
    $('#myDiv').html(data);
  })
</script>

如果已经引入了 jQuery,无疑是最佳方案,没必要重复造轮子了。

方案五: 利用 img 标签

黑魔法,如果后台接口可控的话。

比方说我希望 innerHTML 的内容如下:

<div></div>
<script>
  alert('hello world');
</script>

我们可以这样构造 img:

<div id='myDiv'></div>
<script>
  var str = "<div></div>";
  str += "<img src='empty.gif' onload='alert(\"hello world\"); this.parentNode.removeChild(this);' />";
  document.getElementById('myDiv').innerHTML = str;
</script>

其他: 特殊的 IE

事实上,一定条件下,innerHTML 进来的 script 脚本,在 IE(IE9-?)下是可以触发的。

需要满足的条件如下:

  • 脚本有 defer 属性
  • 脚本并不是 innerHTML 的第一个元素( script 元素必须位于 "有作用域的元素" 之后。如果通过innerHTML 插入的字符串开头就是一个 "无作用域的元素",那么 IE 会在解析这个字符串前先删除该元素)

code:

<body>
<div id='myDiv'></div>
<script type="text/javascript">
  var strVar = "";
  strVar += "<span style='display: none'>hack ie</span><script defer='true'>";
  strVar += "alert('hello world');";
  strVar += "<\/script>";

  document.getElementById("myDiv").innerHTML = strVar;
</script>
</body>

参考:

让 innerHTML 进来的 script 代码跑起来的更多相关文章

  1. 执行 innerHTML 里的 <script>

    原文:执行 innerHTML 里的 <script> 背景 有时候我们会有把一整段 HTML 动态塞进页面的需求,例如渲染了一个模板,从服务器端获取了一段广告代码等.一般情况下我们使用 ...

  2. 出现“不能执行已释放的Script代码”错误的原因及解决办法

    很多web开发者或许都遇到过这样的问题,程序莫名奇怪出现“不能执行已释放Script的代码”,错误行1,列1.对于这种消息描述不着边,行列描述更是让人迷茫的js错误,相信是所有调试js程序的朋友们最郁 ...

  3. 使用innerHTML生成的script节点不会发出请求与执行text属性

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  4. Script 代码段

    script代码段 1.script代码段的执行 在Javascript代码中,可以使用script作为基本标识,script代码段在运行过程中是分段解析与执行的. 2.script代码段执行流程 在 ...

  5. IE10弹窗showModalDialog关闭之后提示SCRIPT5011:不能执行已释放的Script代码

    在Web开发中,经常使用showModalDialog弹窗 今天遇到一个小问题,IE10中弹窗关闭之后提示SCRIPT5011:不能执行已释放的Script代码 网上搜罗了一些资料,发现大多都提到对象 ...

  6. 有一段<script>代码,效果是点击<p>就会弹出信息,但是有的<p>点击会有效果,有的没有效果

    问题:有一段<script>代码,效果是点击<p>就会弹出信息,但是有的<p>点击会有效果,有的没有效果 解决: 页面代码是至上而下执行的,如果你的这个标签在< ...

  7. link标签和script标签跑到body下面,网页顶部有空白

    用UltraEdit的16进制编辑模式查看代码,都是EF BB BF开头的,说明都是带BOM的.我手动的将所有文件转成UTF-8 without BOM.页面终于正常了.link,script标签乖乖 ...

  8. innerHTML动态添加html代码和脚本兼容性问题处理方法

    给某个元素的innerHTML赋值,并使得值中的js代码有效(兼容多个浏览器) 症状:给某个元素的 innerHTML 设置值时,如果提供的 HTML 代码中包含js脚本,很多时候这些脚本无效,或者在 ...

  9. 在纯HTML的静态网页中添加一段统计网页访问量的JAVA Script代码?

    如何在网站上进行流量统计呢,可以找第三方服务网站去注册,但也可以在网站上直接添加代码,只需将以下代码copy到你的网页中,复制到</body>之前就可以啦!是不是很简单啊! <scr ...

随机推荐

  1. pentaho cde数据联动,下拉框,文本框,图形

    先看一下效果: 开源bi工具pentaho数据联动,和传统意义上的更改数据不同,pentaho cde 需要一个监听来动态传值. 说一下需要注意的几个地方吧 1.参数是不能在两个图表中直接传递的,必须 ...

  2. MongoDB ServerStatus返回信息

    ServerStatus返回信息 ServerStatus返回mongodb中很多信息 http://docs.mongodb.org/manual/reference/command/serverS ...

  3. 表单和iframe的使用

    图片热点: 规划出图片上的一个区域,可以做出超链接,直接点击图片区域就可以完成跳转的效果.示例: 网页划区: 在一个网页里,规划出一个区域用来展示另一个网页的内容.示例: 网页的拼接: 在一个网络页面 ...

  4. coursera机器学习笔记-机器学习概论,梯度下降法

    #对coursera上Andrew Ng老师开的机器学习课程的笔记和心得: #注:此笔记是我自己认为本节课里比较重要.难理解或容易忘记的内容并做了些补充,并非是课堂详细笔记和要点: #标记为<补 ...

  5. 烂泥:Linux源码包制作RPM包之Apache

    本文由秀依林枫提供友情赞助,首发于烂泥行天下 公司服务器比较多,需要把apache源码包制作成rpm包,然后放到公司内网yum源上进行下载安装.apache的rpm包安装方式比源码安装方式比较快,这能 ...

  6. WIN 下的超动态菜单(一)

    WIN 下的超动态菜单(一)介绍 WIN 下的超动态菜单(二)用法 WIN 下的超动态菜单(三)代码 作者:黄山松,发表于博客园:http://www.cnblogs.com/tomview/     ...

  7. linux 添加用户、权限

    # useradd –d /usr/sam -m sam 此命令创建了一个用户sam,其中-d和-m选项用来为登录名sam产生一个主目录/usr/sam(/usr为默认的用户主目录所在的父目录). 假 ...

  8. 2-Fedora 17系统安装准备

    在安装Fedora 17系统之前应做好准备工作,这些准备工作包括了解安装系统的硬件要求,Linux系统磁盘分区方案和挂载目录以及制作安装引导盘 学习重点: 硬件要求 硬件兼容性 分区方案 学习内容: ...

  9. Hyper-V架构与VMware ESXi的差异

    微软的Hyper-V与VMware的ESXi在架构上有众多不同,然而很多虚拟化管理员并未意识到这些差异.而且很多管理员同样对Hyper-V是在主机操作系统上运行感到困惑. 有关微软Hyper-V的一个 ...

  10. go语言之并发

    简介           多核处理器越来越普及,那有没有一种简单的办法,能够让我们写的软件释放多核的威力?答案是:Yes.随着Golang, Erlang, Scale等为并发设计的程序语言的兴起,新 ...