Атрибуты и свойства настраиваемых событий веб-компонента Vanilla

Используя веб-компоненты без какой-либо инфраструктуры, как правильно реализовать пользовательское событие? Например, скажем, у меня есть пользовательский элемент x-pop-out, который имеет настраиваемое событие pop. Я бы хотел, чтобы все следующее работало:

<x-pop-out onpop="someGlobal.doSomething()"/>

var el = document.getElementsByTagName('x-pop-out')[0];
el.onpop = ()=> someGlobal.doSomething();
//or
el.addEventListener('pop', ()=> someGlobal.doSomething());

Последнее, что я понимаю, как это сделать, но нужно ли мне настраивать атрибут и геттер/сеттер для каждого? Кроме того, является ли eval() подходящим способом выполнить строку из атрибута?


person hapticdata    schedule 18.02.2017    source источник


Ответы (2)


Решение прослушиватель событий (третье) является самым простым, поскольку вам не нужно определять ничего особенного для перехвата события.

Решения для обработчика событий должны создавать eval() (первое из атрибута) или явно вызывать функцию (второе).

Если вы не можете использовать eval, вы можете вместо этого проанализировать строку атрибута.

customElements.define( 'x-pop-out', class extends HTMLElement {
    connectedCallback() {
        this.innerHTML = `<button id="Btn">pop</button>`

        this.querySelector( 'button' ).onclick = () => {
            this.dispatchEvent( new CustomEvent( 'pop' ) )
            if ( this.onpop )
                this.onpop()
            else
                eval( this.getAttribute( 'onpop' ) )
        }            
    }
} )

XPO.addEventListener( 'pop', () => console.info( 'pop' ) )
<x-pop-out id=XPO onpop="console.log( 'onpop attribute' )"></x-pop-out>
<hr>
<button onclick="XPO.onpop = () => console.log( 'onpop override' )">redefine onpop</button>

person Supersharp    schedule 18.02.2017
comment
Спасибо! это кажется правильным, я задавался вопросом, есть ли какой-то способ зарегистрировать определенное событие во всех трех шаблонах одновременно, но я думаю, вы правы, что это не так. Удивительно думать, что каждый веб-компонент с пользовательским событием должен eval(string), но я не вижу иного предложения. - person hapticdata; 19.02.2017
comment
на самом деле использование eval() связано не с пользовательскими элементами, а со встроенным javascript в атрибутах. Он также используется в стандартных элементах. Вот почему встроенный javascript не разрешен, когда применяется политика ограниченного контента. - person Supersharp; 19.02.2017

На основе ответа SuperSharp. Его предложение будет работать только для журналов консоли, а не для событий. Решение, которое я использовал, состояло в том, чтобы переопределить dispatchEvent и создать собственные атрибуты событий HTML.

Следуя стандарту использования on + event.type. Мы маскируем атрибут в новой функции с помощью with и проверяем, является ли атрибут функцией, и при необходимости передаем событие.

dispatchEvent(event) {
  super.dispatchEvent(event);
  const eventFire = this['on' + event.type];
  if ( eventFire ) {
    eventFire(event);
  } else {
  const func = new Function('e',
    'with(document) {'
       + 'with(this) {'
         + 'let attr = ' + this.getAttribute('on' + event.type) +';'
         + 'if(typeof attr === \'function\') { attr(e)};
       + }'
     + '}'
   );
   func.call(this, event);
   } 
}

Пример:

class UserCard extends HTMLElement {

  constructor() {
    // If you define a constructor, always call super() first as it is required by the CE spec.

    super(); //

  }


  dispatchEvent(event) {
    super.dispatchEvent(event);

    const eventFire = this['on' + event.type];
    if (eventFire) {
      eventFire(event);
    } else {
      const func = new Function('e',

        'with(document) {' +
        'with(this) {' +
        'let attr = ' + this.getAttribute('on' + event.type) + ';' +
        'if(typeof attr === \'function\') { attr(e)};' +
        '}' +
        '}');
      func.call(this, event);
    }

  }

  connectedCallback() {
    this.innerHTML = `<label>User Name</label> <input type="text" id="userName"/>
   <label>Password</label> <input type="password" id="passWord"/>
    <span id="login">login</span>`;
    this.test = this.querySelector("#login");
    this.test.addEventListener("click", (event) => {
      this.dispatchEvent(
        new CustomEvent('pop', {
          detail: {
            username: 'hardcodeduser',
            password: 'hardcodedpass'
          }
        })
      );
    })


  }

}

customElements.define('user-card', UserCard);

function onPop2(event) {
  debugger;
  console.log('from function');
}
<user-card id="XPO" onpop="onPop2"></user-card>
<hr>
<user-card id="XPO" onpop="console.log('direct Console')"></user-card>

person Jordan Hall    schedule 11.04.2018
comment
не работает. Тип attr всегда Object и теперь функция - person ahmadalibaloch; 06.03.2020
comment
@ahmadalibaloch не могли бы вы показать мне ошибку, пожалуйста. Просто запустил фрагмент кода, и он работал нормально - person Jordan Hall; 10.05.2020