使用脚本扩展 HTML 文件并覆盖/扩展某些部分标签
Posted
技术标签:
【中文标题】使用脚本扩展 HTML 文件并覆盖/扩展某些部分标签【英文标题】:Extend HTML file with script and override/extend some section tags 【发布时间】:2016-06-19 03:52:52 【问题描述】:我可以使用开源(客户端)来扩展 html, 例如我需要向它添加脚本或更改一些 src 值并添加额外的标签等。
我发现了以下内容:https://www.npmjs.com/package/gulp-html-extend
但我不确定我是否可以在 client 中使用它(我们在项目中不使用 gulp)我的意思是例如在 jsFiddle。
输入应该是带有新内容的对象/json 的 HTML 内容,输出应该是扩展的 HTML。
如果没有开源,而我需要自己开发,是否有一些我应该从好的设计方面遵循的指导方针?
更新:
例如,如果我将以下 HTML 文档作为 JS 输入变量
这是我得到的字符串输入
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8">
<title>td</title>
<script id="test-ui-bootstrap"
src="resources/test-ui-core.js"
data-test-ui-libs="test.m"
data-test-ui-xx-bindingSyntax="complex"
data-test-ui-resourceroots='"tdrun": "./"'>
</script>
<link rel="stylesheet" type="text/css" href="css/style.css">
<script>
test.ui.get().attachInit(function()
);
</script>
</head>
<body class="testUiBody" id="content">
</body>
</html>
例如,我需要以下内容:
1.
我想在
之后添加额外的脚本(例如,内部带有警报)<script id="test-ui-bootstrap" ....
如果文件中存在id为"test-ui-bootstrap"
的脚本我想在这个脚本之后立即添加另一个脚本,例如
script with alert inside
2.
在最后一个脚本之后的第一个脚本(id id="test-ui-bootstrap")中添加其他属性...
data-test-ui-libs="test.m"
添加
data-test-ui-libs123 ="test.bbb"
3.
如果我想修改现有属性的值,例如改变
src="resources/test-ui-core.js"
到
src="resources/aaaa/test-ui-core.js"
我得到了带有 HTML 的字符串,我需要用修改后的 HTML 创建新字符串我可以用好的方法来做吗?
更新 2
这是 HTML 更改后的输出
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8">
<title>td</title>
<script id="test-ui-bootstrap"
src="resources/aaaa/test-ui-core.js"
data-test-ui-libs="test.m"
data-test-ui-libs123 ="test.bbb"
data-test-ui-xx-bindingSyntax="complex"
data-test-ui-resourceroots='"tdrun": "./"'>
</script>
<script>
alert("test)
</script>
<link rel="stylesheet" type="text/css" href="css/style.css">
<script>
test.ui.get().attachInit(function()
);
</script>
</head>
<body class="testUiBody" id="content">
</body>
</html>
【问题讨论】:
你能把你的问题分解成一个你喜欢做什么的最小例子吗,因为到目前为止它对我来说只是正常的 html 编辑(源代码视图?)。无论如何,我认为现在的问题也很广泛,无法给出一个好的答案 @Icepickle - 请查看我的更新,可以吗? 是的,所以您想解析文本并将其扩展为特定点?当它在浏览器中实时运行时,它不是什么东西,它只是对文本输入的操作?我不确定第二点,如果您将相同的数据名称添加到脚本元素,浏览器引擎现在真的不会如何处理,也许您想扩展操作原始标签? @Icepickle - 是的,这就是我想要的……不,它没有实时运行……抱歉,第二点我需要更改/添加密钥(我会更新我的帖子)谢谢! 我认为您需要的是 DOMParser,请参阅developer.mozilla.org/en-US/docs/Web/API/DOMParser。您可以使用 DOM API 对其进行更新。 【参考方案1】:您可以在 DOM 之外创建一个沙盒元素,然后将您的 HTML 插入其中。
var sandbox = document.createElement('div');
sandbox.innerHTML = yourHTMLString;
浏览器将解析您的 HTML,然后您将能够使用 DOM API 遍历/修改它。
您可以使用它来查找元素并添加属性。
var script = sandbox.querySelectorAll('#test-ui-bootstrap');
script.setAttribute('data-test-ui-libs', 'test.m');
script.setAttribute('src', 'resources/aaaa/test-ui-core.js');
或者在现有元素之后插入新元素。
var newScript = document.createElement('script');
newScript.innerText = 'your script contents';
script.parentNode.insertBefore(newScript, script.nextSibling);
一旦您准备好再次将其作为字符串使用,您就可以将其作为属性读出。
var html = sandbox.innerHTML;
注意。不同的浏览器处理innerHTML
机制的方式不同,当您将HTML 插入沙箱时,您可能会发现它们会去除<body>
和<head>
标记。
如果是这种情况,那么您可以通过 hack 解决它。
var escapedTags = yourHTMLString
.replace(/body/ig, 'body$')
.replace(/head/ig, 'head$')
// now the browser won't recognize the tags
// and therefore won't strip them out.
sandbox.innerHTML = escapedTags;
// do some work
// ...
// don't forget to unescape them!
var unescapedTags = sandbox.innerHTML
.replace(/body\$/g, 'body')
.replace(/head\$/g, 'head');
这利用了浏览器无法理解<body$>
或<head$>
标签是什么的事实,所以它只是原封不动地留下。
【讨论】:
谢谢,您能否在 jsFiddle 中为我写的问题提供示例? @Mark,将您的 HTML 粘贴到输入文本区域中进行测试。 谢谢!但我尝试了它,它对我不起作用,请查看更新 2 以及我想要获得的输出,我需要将 html 作为字符串传递并将扩展的 html 作为字符串返回 谢谢 Dan,但我仍然无法使用我如何获得更新的版本?只是 f5 不起作用。另一个问题能否请您提供一些通用解决方案,例如,在您的解决方案中,如果 html 没有 attr-data-test-ui-libs,您将得到异常,我的需要有点复杂:)我想定义一些具有属性的对象,您可以将其放在那里进行查询和新脚本以及放置它的位置,我需要一些与特定属性或脚本无关的通用解决方案,使用可以定义 json/object有需要,我想阅读它并更改html 我使用的 HTML 和你一样。你怎么了?没有办法为此类特定问题创建通用解决方案。 DOM API 已经是一种用于操作 DOM 节点的领域特定语言,这正是您想要做的。如果您可以编写从 A 到 B 所需的 DOM 操作代码,那么我将尝试派生出更通用的形式。【参考方案2】:你可以使用:
DOMParser
和 XMLSerializer
。
最重要的是;这不是沙盒。它只使用解析器和序列化器;因此它不会执行输入中的脚本;直到将输出注入到实际的 DOM 中。
// HTML string to be modified
var strHTML = '<html>...</html>'; // your HTML
// We'll parse this string into DOM in memory.
var parser = new DOMParser(),
doc = parser.parseFromString(strHTML, 'text/html'),
// in this example, we'll get the script elements and change/set
// some attributes of the first and the content of the second
scripts = doc.getElementsByTagName('script');
scripts[0].setAttribute('data-test-ui-libs123', 'test.bbb');
scripts[0].setAttribute('src', 'resources/aaaa/test-ui-core.js');
scripts[1].innerHTML = 'alert("test")';
// now that we've modified the HTML, we can serialize it into string
var serializer = new XMLSerializer(),
outputHTML = serializer.serializeToString(doc);
Example Pen。
DOMParser 和 XMLSerializer 在 MDN 上。
Browser support:IE10+ 和现代浏览器。
jQuery.parseHTML()
document.implementation.createHTMLDocument()
API 也不执行脚本或通过 HTTP 获取资源(例如视频、图像等)。这是jQuery.parseHTML()
方法使用的方法。见source here。
来自 jQuery 文档;安全考虑:
大多数接受 HTML 字符串的 jQuery API 将运行包含在 HTML 中的脚本。 jQuery.parseHTML 不会在解析后的 HTML 中运行脚本,除非 keepScripts 明确为真。但是,在大多数环境中仍然可以间接执行脚本,例如通过属性。调用者应该意识到这一点,并通过清除或转义来自 URL 或 cookie 等来源的任何不受信任的输入来防范它。为了将来的兼容性,调用者不应依赖于在 keepScripts 未指定或为 false 时运行任何脚本内容的能力。
【讨论】:
谢谢,但您的代码只处理第一个问题,我应该如何以通用方式处理问题 2&3?谢谢! 这应该很明显。您使用诸如element.setAttribute()
之类的DOM API 来设置/更改属性。查看更新的代码和示例笔。
谢谢我检查它,一个关于安全的问题,你的解决方案是不是有点冒险?我应该把 keepScripts 放在哪里?【参考方案3】:
初始(Node.js)
我理解你的问题如下:你想在 Node.js 环境中解析一个 HTML 字符串(你提到 Gulp),扩展它并取回结果字符串。
首先,您需要将字符串解析为一个结构,您可以在该结构上进行查询。有几个库可以实现这一点。 Cheerio.js 被推荐并在 *** answer 中解释。其他解决方案也说明there。然后,该库为您提供 HTML 代码的 DOM 接口。在 Cheerio.js 的示例中,您可以像在 jQuery 中一样访问 DOM。他们的 GitHub 页面的官方示例如下所示。以类似的方式,您可以通过选择元素并添加您的内容(修改它等)来执行您的逻辑。通过调用$.html()
函数,您可以取回修改后的结构。
var cheerio = require('cheerio'),
$ = cheerio.load('<h2 class="title">Hello world</h2>');
$('h2.title').text('Hello there!');
$('h2').addClass('welcome');
$.html();
// => returns '<h2 class="title welcome">Hello there!</h2>'
如果您想在 Gulp 构建过程中使用此逻辑,则需要将其包装到 Gulp 插件中,并以 Cheerio.js 作为依赖项。在这个official GitHub readme file of Gulp 上详细解释了如何创建 Gulp 插件。
编辑(浏览器)
根据您编辑的问题,我将添加有关在浏览器中编辑 HTML 的部分。
在浏览器中使用 jQuery 修改 DOM 非常方便。您还可以使用 jQuery 修改虚拟 DOM。为此,您只需要创建元素而不是将其附加到真实的 DOM 中。不幸的是,当涉及到以下标签时,浏览器会表现得很特别:<html>
、<body>
、<head>
和 <!DOCTYPE html>
。作为一种解决方法,您可以使用正则表达式编辑这些标签并将它们重命名为 <body_temp>
等。你需要有一个好的正则表达式来匹配标签而不是像class="testUiBody"
这样的内容,它也包含body
这个词。特殊行为详细描述here。
以下代码在 HTML 中进行了所有所需的更改。您可以在updated JSFiddle 中对其进行测试。只需单击提交按钮,您就可以看到更改。上面的textarea
用作 HTML 输入,下面的用作 HTML 输出。
var html = "<!DOCTYPE html><html><head><meta.....";
// replace html, head and body tag with html_temp, head_temp and body_temp
html = html.replace(/<!DOCTYPE HTML>/i, '<doctype></doctype>');
html = html.replace(/(<\/?(?:html)|<\/?(?:head)|<\/?(?:body))/ig, '$1_temp');
// wrap the dom into a <container>: the html() function returns only the contents of an element
html = "<container>"+html+"</container>";
// parse the HTML
var element = $(html);
// do your calculations on the parsed html
$("<script>alert(\"test\");<\/script>").insertAfter(element.find('#test-ui-bootstrap'));
element.find("#test-ui-bootstrap").attr('data-test-ui-libs123', "test.bbb");
element.find("#test-ui-bootstrap").attr('src', 'resources/aaaa/test-ui-core.js');
// reset the initial changes (_temp)
var extended_html = element.html();
extended_html = extended_html.replace(/<doctype><\/doctype>/, '<!DOCTYPE HTML>');
extended_html = extended_html.replace(/(<\/?html)_temp/ig, '$1');
extended_html = extended_html.replace(/(<\/?head)_temp/ig, '$1');
extended_html = extended_html.replace(/(<\/?body)_temp/ig, '$1');
// replace all " inside data-something=""
while(extended_html.match(/(<.*?\sdata.*?=".*?)(")(.*?".*?>)/g))
extended_html = extended_html.replace(/(<.*?\sdata.*?=".*?)(")(.*?".*?>)/g, "$1'$3");
// => extended_html contains now your edited HTML
【讨论】:
嗨,这几乎是我需要的,我需要将字符串(带有 html 内容)解析为我可以查询的 HTML,但我需要它在客户端,例如在 jsFiddle 中,你能请提供示例如何使用我在问题中输入的 HTML 在 jsFiddle 中完成,例如在第一个脚本之后添加脚本...我需要获取一些 html 作为字符串并在我根据要求修改后提供修改后的 html我提出问题....谢谢! 嗨,谢谢,但不确定如果我得到你的答案......我需要一种方法来解析这个 html 字符串并查询它并提供新的扩展 html ,你的小提琴太基本了,没有'不回答我的要求...我需要输入的 html,输出应该是扩展的 html...再次感谢 请看我更新的问题,也许现在更清楚了...谢谢! 我更新了代码和小提琴以符合您的要求。您现在可以直接转换您的 HTML 代码。 非常感谢!我有 3 个问题,1 您能否详细说明“浏览器在涉及以下标签时表现得特别:” 2. 您的代码是否安全? 3.如果我没有找到特定的元素/属性/脚本(示例是特定的......)应该如何安全处理?以上是关于使用脚本扩展 HTML 文件并覆盖/扩展某些部分标签的主要内容,如果未能解决你的问题,请参考以下文章