Выполнение JavaScript на импортированном HTML в Firefox - PullRequest
0 голосов
/ 27 октября 2018

Я пытаюсь настроить динамический импорт 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 предыдущем вопросе Stackoverflow мне было предложено использовать это событие как способ узнать, когда импортированный документ успешно добавлен на страницу (ПРИМЕЧАНИЕ. При повторном рассмотрении этого вопроса я замечаю, что 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>.

enter image description here

Возможно, дело в том, чтобы переместить мой 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> внутри заголовка документа, как показано на фотографии выше.

...