使用脚本扩展 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 插入沙箱时,您可能会发现它们会去除&lt;body&gt;&lt;head&gt; 标记。

如果是这种情况,那么您可以通过 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');

这利用了浏览器无法理解&lt;body$&gt;&lt;head$&gt; 标签是什么的事实,所以它只是原封不动地留下。

【讨论】:

谢谢,您能否在 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】:

你可以使用:

DOMParserXMLSerializer

最重要的是;这不是沙盒。它只使用解析器和序列化器;因此它不会执行输入中的脚本;直到将输出注入到实际的 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 不会在解析后的 H​​TML 中运行脚本,除非 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 中。不幸的是,当涉及到以下标签时,浏览器会表现得很特别:&lt;html&gt;&lt;body&gt;&lt;head&gt;&lt;!DOCTYPE html&gt;。作为一种解决方法,您可以使用正则表达式编辑这些标签并将它们重命名为 &lt;body_temp&gt; 等。你需要有一个好的正则表达式来匹配标签而不是像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 &quot; inside data-something=""
while(extended_html.match(/(<.*?\sdata.*?=".*?)(&quot;)(.*?".*?>)/g)) 
  extended_html = extended_html.replace(/(<.*?\sdata.*?=".*?)(&quot;)(.*?".*?>)/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 文件并覆盖/扩展某些部分标签的主要内容,如果未能解决你的问题,请参考以下文章

chrome extension插件扩展开发部分文档

chrome extension插件扩展开发部分文档

TYPO3 tx_news 扩展不使用覆盖模板

错误 - 在Windows 7中使用“Git Bash Here”时,“文件扩展名.vbs没有脚本引擎”

Django:覆盖和扩展应用程序模板

GRUB 脚本中的文件名通配符扩展