JS> привязать событие HTML onclick к объекту пользовательского элемента v1
TL; DR
Как я могу привязать обратный вызов встроенного onclick-события элемента div к пользовательскому элементу, в который помещен div?
Я хочу использовать onclick="foo('bar')" вместо onclick="this.foo('bar')".
длинная версия :
Добавив пользовательский элемент с кликабельным div к DOM следующим образом:
<my-element>
<!-- 1. works -->
<div onclick="this.foo('bar')">click me</div>
<!-- 2. not working -->
<div onclick="foo('bar')">click me</div>
<!-- edit: 3. playground (works) -->
<div onclick="me.foo('bar')">click me</div>
</my-element>
... теперь я хочу привязать foo()-функцию к моему пользовательскому элементу (<my-element>).
1-е решение : здесь onclick вызывает foo() - функцию на this (this.foo()), где this позже привязывается к моему пользовательскому элементу (см. следующий код).
2-е решение : здесь я хочу опустить this. и снова привязать его к моему пользовательскому элементу. Но: пока связывание работает в вышеуказанном решении (1.), это не без предлога this. - вот в чем моя проблема.
Отредактированное 3-е решение : использует прокси для проецирования вызова функции из me в this пользовательский элемент. Это не решает проблему, но ... по крайней мере, я пытался.
Итак, проблема заключается в следующем: Как заставить решение 2 работать?
Мой пользовательский элемент v1 класс-включая некоторый код, который я попробовал:
class MyElement extends HTMLElement
{
constructor()
{
super()
}
connectedCallback()
{
var self = this
this.addEventListener('click', this.handleClick, true)
// Proxy for solution 3
window.me = new Proxy({},
{
get: function(target, prop)
{
return self[prop]
}
})
}
handleClick(ev)
{
console.log(ev.target.onclick)
// returns: ƒ onclick(event) { foo("bar") }
// > is typeof function
// provides the binding to "this" for solution 1
ev.target.onclick = ev.target.onclick.bind(this)
// call
ev.target.onclick()
// works on first solution
// throws error in 2nd solution: foo is not defined
}
foo(str)
{
console.log(str)
}
}
customElements.define('my-element, MyElement)
Некоторые пояснения:
-
Используя
addEventListenerи установив его 3-й параметр (useCapture) в true, я фиксирую событие перед выполнением. -
Я могу записать
foo()-функцию в консоль. Он инкапсулирован в вызываемую функцию, которая, по-видимому, является причиной, почему я не могу связать свой контекст к самой функцииfoo(). -
Вызов функции через
ev.target.onclick()вызывает ошибку, так как в данном контексте (window) никакойfoo()-функции не существует.
Некоторые мысли:
React делает что-то похожее (я еще не использовал React), но я не вижу, как передать их решение, поскольку они в конечном итоге каким-то образом предварительно обрабатывают свои события onclick (с eval?). Это может быть причиной, почему их синтаксис
onclick={foo}вместоonclick="foo()". Я также не знаю, возможна ли передача параметров в React. Смотрите: https://facebook.github.io/react/docs/handling-events.html-
В связи с этим может возникнуть проблема, что в моем решении функция
foo(), вероятно, уже каким-то образом вызвана внутри контекста окна, что означает, что ее контекст уже установлен и больше не может быть изменен...? В любом случае, попытка установитьonclick="foo"без скобок и вызвать его позже тоже не работает.
Хорошо прочитайте эту тему:
Http://reactkungfu.com/2015/07/why-and-how-to-bind-methods-in-your-react-component-classes/
Итак, я не знаю, есть ли вообще решение. Идеи или объяснения приветствуются в любом случае!Правка 2: исправлено.
Он также работает, когда элемент создается и добавляется из класса элемента:
var el = document.createElement('div')
el.innerHTML = 'click me'
el.onclick = () => {
this.foo('bar')
}
this.appendChild(el)
Правка 3, 4, 5:
Немного поиграл (см. Решение 3) и назначил прокси-объект глобальной переменной ('me'), который проецирует любые вызовы функций на this пользовательский элемент - kinda __noSuchMethod__. Так... выбрав 1-или 2-символьную переменную, можно сэкономить по крайней мере несколько нажатий на клавиатуре, не набирая this., но me., vm. или даже i.... пока что это лучшее решение. Грустно!
К сожалению, решение 3 также не может быть использовано в качестве общего шаблона для всех элементов, так как оно ограничивает контекст только одним (т. е. последним подключенным) пользовательским элементом.
[44]}правка 6-предварительные выводы
Как оказалось, привязать HTML-inline onclick-event (onclick="foo('bar')") к классу пользовательского элемента, в который встроен кликабельный элемент, не представляется возможным.
onclick="this.foo('bar')") и свяжите его с классом, как показано выше. Почему this.foo() связывается, тогда как foo() Без this нет, остается неясным на данный момент. Интересно, что обе функции на самом деле не отличаются - у обеих уже есть привязки: this.foo() привязывается к кликабельному элементу, foo() привязывается к window.
B) используйте eval, как предлагает @Supersharp, то есть добавьте прослушиватель событий в пользовательский элемент, прослушайте клики, получите значение атрибута onclick в виде строки, добавьте "this."к струне и эффективно сделать это: eval("this." + "foo()").
C) в качестве альтернативы встроенному onclick я бы предпочел использовать делегирование событий и декларативные атрибуты html для указания поведения. Для этого снова добавьте прослушиватель событий click в Пользовательский класс элемента:
myElement.addEventListener('click', function(ev) {
if (ev.target.dataset.action)
{
this[ev.target.dataset.action]()
}
}, false)
Ваш кликабельный элемент будет выглядеть как <div data-action="next">Show more</div>.
1 ответ:
Решение состоит в использовании
eval()на конкатенации"this."и содержании атрибутаonclickэлементаevent.target.Не забудьте использовать
event.stopPropagation(), чтобы остановить отправку события.handleClick( ev ) { eval( 'this.' + ev.target.getAttribute( 'onclick' ) ) ev.stopPropagation() }Если вы не хотите использовать
eval(), Вы должны проанализировать содержимоеev.target.getAttribute( 'onclick' ), чтобы извлечь имя функции et аргументы, а затем вызвать метод элемента, если он существует:if ( typeof this.name === 'function ) this[name](arguments)Обновить
Я предлагаю вам дать
idпользовательскому элементу, а затем вызвать идентификатор + метод:<my-element id="me"> <div onclick="me.foo('bar')">click me</div> </my-element>Таким образом, вам не нужно перенаправлять/перехватывать событие, и
divможет быть в любом месте элемента.