Выполнение JavaScript на импортированном HTML в Firefox
Я пытаюсь настроить динамический импорт HTML для работы в одностраничном веб-приложении. Я не использую какие-либо рамки для этого. Я могу добиться правильного поведения в Chrome. Тем не менее, я испытываю неправильное поведение в Firefox (и даже не начинаю с IE).
Я загружаю 2 полизаполнения: полифилл / загрузчик веб-компонентов, а также полифилл HTML-импорта, как указано в предыдущем ответе на вопрос, который я разместил.
После долгих проб и ошибок в Chrome я смог успешно импортировать целевой HTML в мой основной документ, index.html
и получить встроенный JavaScript в импортированном документе для выполнения в DOM импортированного HTML в основном документе (в отличие от DOM импорта HTML).
Проблема возникает, когда я пытаюсь сделать то же самое в Firefox. Кажется, что встроенный JavaScript не выполняется в DOM основного документа. Вместо этого он действует на импортированных страницах DOM.
Вот код:
// index.html
...
<body>
// load the polyfills
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script src="src/scripts/helper/html-imports.min.js"></script>
<header> ... </header>
<nav> ... </nav>
<main>
/* Import Documents get appended to the <main> element */
</main>
// A Webpack-derived bundle including init.js
<script src="bundle-main.js"></script>
</body>
Логика JavaScript для приложения начинается с init.js
(часть пакета Webpack), которая импортирует вспомогательную функцию, которая обрабатывает динамический импорт HTML.
// init.js
import {importHTML} from './helper/helper.js';
const APP_PATH = 'app.html';
const FORM_PATH = 'form.html';
(function(){
... some logic ...
importHTML(FORM_PATH);
})();
// helper.js
/**
* Dynamically imports HTML into the Main file
*
* @param {String} path The path to the document to import
* @return {null}
*/
export async function importHTML(path) {
let link = document.createElement('link');
link.rel = 'import';
link.href = path;
link.setAttribute('async', '');
link.onload = (e) => {
console.log('Success loading', e, e.target.href);
let importDoc = document.querySelector('link[rel="import"]').import;
let formHTML = importDoc.querySelector('#formContainer');
// get handle to <main> in document body
let target = document.body.querySelector('main');
target.appendChild(formHTML.cloneNode(true));
}
link.onerror = (e) => { console.error('Error loading', e, e.target.href); }
document.head.appendChild(link);
}
В этом случае импортированный документ form.html
, имеет HTML-разметку для формы, а затем выполняет сценарий JS внизу страницы.
// form.html
<section id="formContainer">
<style>
... embedded styles ...
</style>
<!-- load a compiled webpack bundle to handle with MDC SASS styles -->
<link rel="stylesheet" href="bundle-form.css">
<div id="splash">
<h1>App Title</h1>
<p>... desc ...</p>
<button type="button">Get Started</button>
</div>
<form action="" method="POST">
<ul>
<li>
<p class="form-question-desc">Select your gender:</p>
<div class="mdc-form-field question-container">
<label for="male">Male: </label>
<div class="mdc-radio">
<input class="mdc-radio__native-control" type="radio" name="gender" id="male" checked>
<div class="mdc-radio__background">
<div class="mdc-radio__outer-circle"></div>
<div class="mdc-radio__inner-circle"></div>
</div>
</div>
<label for="female">Female: </label>
<div class="mdc-radio">
<input class="mdc-radio__native-control" type="radio" name="gender" id="female">
<div class="mdc-radio__background">
<div class="mdc-radio__outer-circle"></div>
<div class="mdc-radio__inner-circle"></div>
</div>
</div>
</div>
</li>
<li>
<p class="form-question-desc">How old are you?</p>
<div class="text-field-container question-container" data-mdc-auto-init="MDCTextField">
<div class="mdc-text-field text-field mdc-text-field--upgraded">
<input type="number" id="age" name="age" class="mdc-text-field__input" value="33" required>
<label class="mdc-floating-label" for="age">Age: </label>
<div class="mdc-line-ripple" style="transform-origin: 31.5px center 0px;"></div>
</div>
</div>
</li>
<li>
... more markup using MDC classes/components ...
</li>
... other <li> elements ...
</ul>
<div id="submitForm">
<input type="submit" value="All Done!" class="app-theme button mdc-button mdc-button--raised" />
</div>
</form>
<div class="pagination">
<span id="pageBack" class="pagination-icon"><img src="icons/left-chevron.svg" alt="Arrow left"/></span>
<span id="pageForward" class="pagination-icon"><img src="icons/right-chevron.svg" alt="Arrow right"/></span>
</div>
<!-- execute script that handles form behavior -->
<script src="bundle-form.js" defer></script>
</section>
В bundle-form.js
У меня есть функция, которая слушает HTMLImportsLoaded
событие, прежде чем я начну манипулировать импортированными HTML DOM
, В предыдущем вопросе Stackru мне было предложено использовать это событие как способ узнать, когда импортированный документ успешно добавлен на страницу (ПРИМЕЧАНИЕ. При повторном рассмотрении этого вопроса я замечаю, что HTMLImportsLoaded
слушатель события находится в основном документе, index.html
, а не импортированный документ, как в моем текущем примере здесь. Это может быть ключом.)
После настройки различных инициализаций элементов в form.js
, Я звоню setQuestionInitState
который скрывает все, кроме первого вопроса формы, а также скрывает <form>
элемент, показывающий только заставку и кнопку, чтобы начать.
// form.js
let splashPage, form, formQuestions, submitBtn,
.. more variable declarations ...;
/**
* Sets initial state of form questions by
* hiding all but the first question
*
* @param {HTMLCollection} allQuestions A collection of LI elements
*/
let setQuestionInitState = (allQuestions) => {
let index = 0;
for (let question of allQuestions) {
if (index != 0) {
question.setAttribute('hidden', true);
}
index++;
}
}
/**
* Hide the splash page and display the form
* @return {[type]} [description]
*/
let startForm = () => {
splashPage.setAttribute('hidden', '');
form.removeAttribute('hidden');
}
window.addEventListener('HTMLImportsLoaded', function() {
// get reference to the form element in the main document
form = document.forms[0];
formQuestions = form.querySelector('ul').children;
// hide the form and the submit button
form.setAttribute('hidden','');
submitBtn.setAttribute('hidden', '');
splashPage = document.body.querySelector('#splash')
let splashBtn = document.body.querySelector('#splash > button')
splashBtn.addEventListener('click', startForm);
... more assignments and initializations ...
setQuestionInitState(formQuestions);
});
В Chrome эта последовательность успешно ждет, пока импорт HTML загрузит свой DOM в основной документ, а затем приступит к работе над этим DOM через JS в form.html
, В нем успешно скрываются правильные вопросы и форма.
В Firefox, однако, это не работает так же. Вместо этого он применяет логику JavaScript к импортированному документу (это может быть неправильной терминологией для этого "фрагмента документа" этого импорта). DOM основного документа не зависит от form.js
непосредственно. Я вижу, что это касается виртуального DOM, если я проверяю документ <head>
,
Возможно, дело в том, чтобы переместить мой HTMLImportsLoaded
в index.html
но это идет вразрез с модульным подходом, который я пытаюсь поддерживать (сохраняя мой JS для импортированного HTML внутри импортированного документа). Это также потребует еще более динамичного подхода к обработке импорта HTML и связанных с ним сценариев /css, вероятно, происходящих из некоторых умных сценариев в index.html
,
Я приветствую все идеи и заранее благодарю за любой совет, который вы можете предложить.
Обновить
Как рекомендовал комментатор, я попытался переместить HTMLImportsLoaded
слушатель события импортирующего документа, index.html
и динамически загрузить сопутствующий скрипт для импортированного документа:
//index.html
window.addEventListener('HTMLImportsLoaded', function() {
console.log('HTMLImportsLoaded loaded from main document');
let formJS = document.createElement('script');
formJS.src = 'bundle-form.js';
formJS.type = 'text/javascript';
if (document.body.appendChild(formJS)) {
console.log('Script was appended');
}
});
В chrome это работало нормально, однако в Firefox javascript не действует на DOM импортируемого документа. Это, однако, похоже, влияет на DOM при проверке <link>
элемент в заголовке документа, как показано на фотографии выше.