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
может быть в любом месте элемента.