Расширьте HTML файл с помощью script и переопределите/расширьте теги раздела
Существует открытый исходный код (клиентская сторона), который я могу использовать для расширения HTML,
например, мне нужно добавить к нему сценарии или изменить некоторые значения src и добавить дополнительные теги и т.д.
Я нашел следующее: https://www.npmjs.com/package/gulp-html-extend
но я не уверен, могу ли я использовать его в клиенте (мы не используем gulp в нашем проекте). К клиенту я имею в виду, например, использовать его в jsFiddle.
Ввод должен быть содержимым HTML с некоторым объектом /json с новым контентом, а вывод должен быть расширен HTML.
Если нет открытого источника, и мне нужно его самостоятельно разработать, есть ли какая-то направляющая строка, которую я должен придерживаться из хороших аспектов дизайна?
UPDATE:
Например, если у меня есть следующий 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 (например, с предупреждением внутри) после
<script id="test-ui-bootstrap" ....
если в файле script есть id "test-ui-bootstrap"
Я хочу добавить сразу после этого script еще один script например.
script with alert inside
2.
Чтобы добавить дополнительное свойство внутри первого script (с id id = "test-ui-bootstrap" ) после последнего script...
data-test-ui-libs="test.m"
Чтобы добавить
data-test-ui-libs123 ="test.bbb"
3.
Если я хочу изменить значение существующего свойства, например. изменение
src="resources/test-ui-core.js"
to
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>
Ответы
Ответ 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
по-разному, и вы можете обнаружить, что они стягивают теги <body>
и <head>
при вставке вашего HTML в вашу песочницу.
Если это так, вы можете обойти это с помощью взлома.
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$>
, поэтому он просто уходит в неповрежденном состоянии.
Ответ 2
НАЧАЛЬНАЯ (Node.js)
Я понимаю ваш вопрос следующим образом: вы хотите проанализировать HTML-строку в среде Node.js(вы упомянули Gulp), расширить ее и вернуть результирующую строку.
Сначала вам нужно проанализировать строку в структуре, на которой вы можете создавать запросы. Для этого есть несколько библиотек. Cheerio.js был рекомендован и объяснен в fooobar.com/questions/80372/.... Другие решения также объясняются там. Библиотека предоставляет вам интерфейс DOM вашего HTML-кода. В примере с Cheerio.js вы можете получить доступ к DOM аналогично JQuery. Официальный пример их страницы 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 в качестве зависимости. В этом официальном файле readme от GitHub Gulp подробно объясняется, как вы можете создать плагин Gulp.
EDIT (браузер)
В соответствии с вашим отредактированным вопросом я добавлю этот раздел об изменении HTML в браузере.
Очень удобно использовать jQuery для изменения DOM в браузере. Вы также можете изменить виртуальную DOM с помощью jQuery. Для этого вам просто нужно создать элемент, но не добавить его в реальный DOM. К сожалению, браузер действует специально, когда дело доходит до следующих тегов: <html>
, <body>
, <head>
и <!DOCTYPE html>
. В качестве обходного пути вы можете просто редактировать эти теги с регулярным выражением и переименовывать их в нечто вроде <body_temp>
и так далее. Вы должны иметь хорошее регулярное выражение, чтобы соответствовать только тегам, а не содержимому, например class="testUiBody"
, которое также содержит слово body
. Особое поведение подробно описано здесь.
Следующий код делает все необходимые изменения в HTML. Вы можете протестировать его в обновленном 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
Ответ 3
Вы можете использовать:
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);
jQuery.parseHTML()
API document.implementation.createHTMLDocument()
также не выполняет сценарии или извлекает ресурсы через HTTP (например, видео, изображения и т.д.). Это подход, используемый методом jQuery.parseHTML()
. Смотрите источник здесь.
Из jQuery docs; соображения безопасности:
Большинство jQuery API, которые принимают строки HTML, будут запускать скрипты, которые включены в HTML. jQuery.parseHTML не запускает скрипты в анализируемом HTML, если keepScripts явно не верен. Тем не менее, по-прежнему возможно в большинстве сред выполнять сценарии косвенно, например, через атрибут. Вызывающий должен знать об этом и защищаться от него, очищая или избегая любых ненадежных входов из таких источников, как URL-адрес или файлы cookie. Для будущей совместимости вызывающие абоненты не должны зависеть от возможности запуска любого содержимого script, если keepScripts не указано или false.