У меня проблема с работой dispatchEvent. Я пытаюсь избавиться от зависимости от jQuery и делаю собственные небольшие расширения для собственного API, но не могу заставить мою версию trigger работать с dispatchEvent.
После того, как консоль все записала, я знаю, что eventType передается, объект event создается, а this относится к правильному целевому элементу. Вот код, который у меня есть, буду благодарен за любую помощь.
if (!EventTarget.prototype.trigger) {
function triggerFunction(
eventType) {
const event = new CustomEvent(eventType, {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
}
EventTarget.prototype.trigger = triggerFunction;
}
Редактировать
Вот остальной код. На самом деле ничего особенного, потому что я в основном сосредоточился на создании своих расширений.
if (!EventTarget.prototype.on) {
function onFunction(
eventType,
targetElement,
listener) {
if (targetElement) {
this.addEventListener(eventType, function (
event) {
const target = event.target;
if (target
&& target.nodeName !== targetElement) {
return;
}
listener.call(target, event);
}, false);
return this;
}
this.addEventListener(eventType, listener, false);
return this;
}
EventTarget.prototype.on = onFunction;
}
const _pointerTap = "click";
const photosFunction = () => {
const $buttonPhotosUpload = document.getElementById("button-photos-upload"),
$fieldPhotosUpload = document.getElementById("field-photos-upload");
const onButtonPhotosUploadTap = () => $fieldPhotosUpload.trigger(_pointerTap);
$buttonPhotosUpload.on(_pointerTap, null, onButtonPhotosUploadTap);
};
document.getElementById("photos") && photosFunction();
И HTML:
<div id = "photos">
<div>
<button type = "button" id = "button-photos-upload">{UPLOAD}</button>
<input type = "file" id = "field-photos-upload" accept = "image/*" multiple />
</div>
</div>
В основном, когда кнопка нажата, я хочу вызвать событие щелчка файла input:, чтобы я мог визуально скрыть его с помощью CSS. В моей версии jQuery это работает нормально.
Я добавил остальную часть кода, ничего особенного, так как я только начинал.
Я добавил функцию on, _pointerTap там был, наверное не выбирал при копировании / вставке. Я обновил скрипку, и она ведет себя так же, как и мой проект, то есть не запускает щелчок. jsfiddle.net/n2jbskh8/1



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вот гораздо более минималистичный MCVE:
function triggerFunction(eventType) {
const event = new CustomEvent(eventType, {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
}
if (!EventTarget.prototype.trigger) {
EventTarget.prototype.trigger = triggerFunction;
}
const button = document.querySelector('button');
const input = document.querySelector('input');
button.addEventListener('click', () => {
// input.click(); // Works
// input.trigger('click'); // Doesn't work
});<div>
<button type = "button">{UPLOAD}</button>
<input type = "file">
</div>Проблема в том, что отправленное событие не запускает функцию (встроенный в браузер), которая открывает диалоговое окно ввода. Отправленное событие делает запускает другие обычные слушатели.
Я сомневаюсь, что есть какой-либо способ сделать это без вызова встроенной функции click, которая вызывает собственный код браузера, который позволяет открывать диалоговое окно. Одна из возможностей - проверить, является ли eventTypeclick, а EventTarget - HTMLElement. Кажется, что можно обойти проблему, но я не думаю, что есть другой способ, когда цель требует вызова собственный код браузера для диалога.
function triggerFunction(eventType) {
if (this instanceof HTMLInputElement && eventType === 'click') {
return HTMLElement.prototype.click.call(this);
}
const event = new CustomEvent(eventType, {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
}
if (!EventTarget.prototype.trigger) {
EventTarget.prototype.trigger = triggerFunction;
}
const button = document.querySelector('button');
const input = document.querySelector('input');
button.addEventListener('click', () => {
input.trigger('click'); // Works
});<div>
<button type = "button">{UPLOAD}</button>
<input type = "file">
</div>Аналогичный шаблон потребуется при вызове любого метода, который требует запуска собственного кода (например, console.info или XMLHttpRequest, если fetch недоступен, или setTimeout - некоторые вещи просто невозможно эмулировать с помощью вашего собственного Javascript и требуют вызова некоторого собственного кода. браузера).
Также обратите внимание, что похоже, что вы просто пытаетесь добавить собственные методы в HTML-элементы, и в этом случае было бы неплохо назначить trigger для HTMLElement.prototype, а не EventTarget.prototype:
function triggerFunction(eventType) {
if (this instanceof HTMLInputElement && eventType === 'click') {
return HTMLElement.prototype.click.call(this);
}
const event = new CustomEvent(eventType, {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
}
if (!HTMLElement.prototype.trigger) {
HTMLElement.prototype.trigger = triggerFunction;
}
const button = document.querySelector('button');
const input = document.querySelector('input');
button.addEventListener('click', () => {
input.trigger('click'); // Works
});<div>
<button type = "button">{UPLOAD}</button>
<input type = "file">
</div>На самом деле dispatchEvent (MouseEvent) сработал бы.
Дело в том, что браузеры не только смотрят на свойство type вашего события, они также требуют, чтобы оно унаследовало от правильного подкласса Event.
В этом случае единственный подкласс Event, который им нужен, - это MouseEvent, поэтому вместо dispatchEvent(new CustomEvent('click')); вам придется использовать dispatchEvent(new MouseEvent('click')).
(MCVE заимствовано из ответа @CertainPerformance).
function triggerMouseFunction(eventType) {
// here event must be MouseEvent for it to work on <input>
const event = new MouseEvent(eventType, {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
}
if (!EventTarget.prototype.triggerMouse) {
EventTarget.prototype.triggerMouse = triggerMouseFunction;
}
const button = document.querySelector('button');
const input = document.querySelector('input');
button.addEventListener('click', () => {
input.triggerMouse('click'); // Works
});<div>
<button type = "button">{UPLOAD}</button>
<input type = "file">
</div>Но если все, что вам нужно, это действительно вызвать событие щелчка, то непременно используйте HTMLElement.click().
Нет необходимости ни в какой библиотеке, ни в изменении прототипа EventTarget, что является очень плохой идеей.
const button = document.querySelector('button');
const input = document.querySelector('input');
button.addEventListener('click', () => {
input.click(); // Works
});<div>
<button type = "button">{UPLOAD}</button>
<input type = "file">
</div>Спасибо, вот и все! Я добавил функцию getEvent, чтобы включить eventType и вернуть правильный объект. Мне нужно, чтобы он обрабатывал не только событие щелчка, потому что в конечном итоге я хочу удалить свою зависимость от jQuery для этого проекта. Еще раз спасибо!
Благодаря @Kaiido, теперь он снова работает. Я добавил функцию getEvent, чтобы включить eventType и вернуть правильный объект Event. Обновленный код выглядит так:
function getEvent(
eventType): Event {
switch (eventType) {
case "keydown":
case "keypress":
case "keyup":
return new KeyboardEvent(eventType);
case "click":
case "dblclick":
case "mouseup":
case "mousedown":
return new MouseEvent(eventType);
default:
return new CustomEvent(eventType);
}
}
function triggerFunction(
eventType) {
const event = getEvent(eventType);
event.bubbles = true;
event.cancelable = true;
this.dispatchEvent(event);
}
if (!EventTarget.prototype.trigger) {
EventTarget.prototype.trigger = triggerFunction;
}
У меня отлично работает. jsfiddle.net/gftey7qs Если вы все еще испытываете это, можете ли вы отредактировать свой вопрос, чтобы показать, какой другой код вы используете (например, прикрепленный прослушиватель событий, который не запускается должным образом), чтобы у нас был минимальный воспроизводимый пример для отладки?