Как добавить JavaScript в пользовательские элементы?

У меня есть следующий код, который создает пользовательский элемент, инкапсулированный с Shadow DOM:

'use strict'
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {

    var root = this.createShadowRoot();
    var divEl = document.createElement('div');
    divEl.setAttribute("id", "container");
    divEl.innerHTML =
        "<input id='input' type='text'>"
      + "<br>"
      + "Result: <span id='result'></span>"
      + "<br><button onclick='performTask()'>Run</button>";
    root.appendChild(divEl);

};
document.registerElement('custom-ele', {
    prototype: proto
});

Идея состоит в том, что при нажатии "Выполнить" ввод будет взят из элемента ввода и обработан (в executeTask()), а затем вывод помещен в "#result". Мои два вопроса:

  1. Как мне получить значение из поля ввода в Shadow DOM?
  2. Как бы я поместил вывод в #result?

Это предыдущее сообщение о переполнении стека выглядит так, как будто оно ответило бы на мой вопрос, но все предложенные ссылки больше не действительны, поэтому мне интересно, может ли кто-нибудь указать мне правильное направление:)

PS Я бы предпочел не использовать шаблоны, так как импорт HTML поддерживается не всеми браузерами, и я хочу, чтобы весь код пользовательского элемента содержался в одном файле.

2 ответа

Решение

Оказывается, вы можете добавлять функции в сам теневой корень, затем вы можете просто вызвать this.parentNode.fn() для теневых корней, чтобы дети получили доступ к shadowRoot...

proto.createdCallback = function() {

    let root = this.createShadowRoot();

    root.innerHTML = "<input id='input' type='text'>"
        + "<br>"
        + "Result: <span id='result'></span>"
        + "<br><button onclick='this.parentNode.process()'>Run</button>";

    this.shadowRoot.process = function() {
        let spanEle = this.querySelector('span');
        let inputEle = this.querySelector('input');
        spanEle.textContent = performAlgorithm(inputEle.value.split(','));
    };
};
document.registerElement('custom-ele', { prototype: proto });

(Спасибо MarcG за предоставленную мне начальную информацию)

С ЗАКРЫТИЕМ

Вы можете использовать метод querySelector на вашем Shadow DOM root попасть внутрь элементов:

'use strict'
var proto = Object.create( HTMLElement.prototype )
proto.createdCallback = function ()
{
    //HTML ROOT
    var root = this.createShadowRoot()
    root.innerHTML = "<input id='input' type='text'>"
        + "<br>"
        + "Result: <span id='result'></span>"
        + "<br><button>Run</button>"

    //UI
    var buttonEle = root.querySelector( "button" )
    var inputEle = root.querySelector( "input" )
    var spanEle = root.querySelector( "#result" )
    buttonEle.onclick = function ()
    {
        var input = inputEle.value
        // do some processing...
        spanEle.textContent = input
    } 
}
document.registerElement( 'custom-ele', { prototype: proto } )

NB: вы можете использовать template без импорта HTML, на той же странице. Смотрите следующий фрагмент:

<html>

<body>
  <custom-ele></custom-ele>

  <template id="custelem">
    <input id='input' type='text'>
    <br>Result:
    <span id='result'></span>
    <br>
    <button>Run</button>
  </template>

  <script>
    var proto = Object.create(HTMLElement.prototype)
    proto.createdCallback = function() {
      //HTML ROOT
      var root = this.createShadowRoot()
      root.innerHTML = custelem.innerHTML

      //UI
      var buttonEle = root.querySelector("button")
      var inputEle = root.querySelector("input")
      var spanEle = root.querySelector("#result")
      buttonEle.onclick = function() {
        var input = inputEle.value
          // do some processing...
        spanEle.textContent = input
      }
    }
    document.registerElement('custom-ele', {
      prototype: proto
    })
  </script>


</body>

</html>

БЕЗ ЗАКРЫТИЯ

Если вы не хотите использовать замыкание, вы можете объявить методhandleEvent на свой пользовательский элемент и добавьте прослушиватель событий, который будет перенаправлять на него:

proto.createdCallback = function ()
{
    //HTML ROOT
    var root = this.createShadowRoot()
    root.innerHTML = custelem.innerHTML

    //EVENT
    var buttonEle = root.querySelector( "button" )
    buttonEle.addEventListener( "click", this )
}

proto.handleEvent = function ( ev )
{
    var inputEle = this.shadowRoot.querySelector( "input" )
    var spanEle = this.shadowRoot.querySelector( "#result" )
    // do some processing...
    spanEle.textContent = inputEle.value
}
Другие вопросы по тегам