I'm having an issue with getting dispatchEvent
to work. I'm trying to remove my dependency on jQuery, and have been making my own little extensions to the native API, but I can't get my version of trigger
to work using dispatchEvent
.
After console logging everything, I know that the eventType
is being passed in, the event
object is being created, and this
refers to the correct target element. Here's the code I've got, I'd appreciate any help.
if (!EventTarget.prototype.trigger) {
function triggerFunction(
eventType) {
const event = new CustomEvent(eventType, {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
}
EventTarget.prototype.trigger = triggerFunction;
}
Edit
Here's the rest of the code. There's really nothing much because I was mostly focusing on building up my extensions.
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();
And the 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>
Basically when the button is pressed, I want to trigger the click event of the input:file so I can hide it visually with CSS. In my jQuery version this works just fine.
I'm having an issue with getting dispatchEvent
to work. I'm trying to remove my dependency on jQuery, and have been making my own little extensions to the native API, but I can't get my version of trigger
to work using dispatchEvent
.
After console logging everything, I know that the eventType
is being passed in, the event
object is being created, and this
refers to the correct target element. Here's the code I've got, I'd appreciate any help.
if (!EventTarget.prototype.trigger) {
function triggerFunction(
eventType) {
const event = new CustomEvent(eventType, {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
}
EventTarget.prototype.trigger = triggerFunction;
}
Edit
Here's the rest of the code. There's really nothing much because I was mostly focusing on building up my extensions.
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();
And the 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>
Basically when the button is pressed, I want to trigger the click event of the input:file so I can hide it visually with CSS. In my jQuery version this works just fine.
Share Improve this question edited Jan 11, 2019 at 3:35 Gup3rSuR4c asked Jan 11, 2019 at 1:08 Gup3rSuR4cGup3rSuR4c 9,48814 gold badges78 silver badges133 bronze badges 3- 3 Works fine for me. jsfiddle/gftey7qs If you're still experiencing it, can you edit your question to show what other code you're using (such as an attached event listener that isn't firing as desired), so we have a minimal reproducible example to debug? – CertainPerformance Commented Jan 11, 2019 at 1:11
- I've added the rest of the code, there wasn't much since I was just starting out on this. – Gup3rSuR4c Commented Jan 11, 2019 at 3:02
-
I've added the
on
function,_pointerTap
was there, probably didn't select it when copy/pasting. I've updated the fiddle, and it behaves the same ways as my project, that is it's not triggering the click. jsfiddle/n2jbskh8/1 – Gup3rSuR4c Commented Jan 11, 2019 at 3:37
3 Answers
Reset to default 3The thing is that browsers don't only look at the type
property of your Event, they also require it to inherits from the correct Event sub-class.
In this case, the one sub-class of Event they want is a MouseEvent, so instead of dispatchEvent(new CustomEvent('click'));
, you'd have to do dispatchEvent(new MouseEvent('click'))
.
(MCVE borrowed from @CertainPerformance answer).
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>
But if all you want is really to trigger a click event, then by all means just use HTMLElement.click()
.
No need for any lib, nor for modifying the prototype of EventTarget, which is a real bad idea.
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>
Here's a much more minimal 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>
The problem is that the dispatched event doesn't trigger the (browser's built-in) function that opens the input dialog. The dispatched event does trigger other normal listeners.
I'm doubtful that there's any way to do it without calling the built-in click
function that invokes the browser's native code that allows the dialog box to e up. One possibility would be to check if the eventType
is click
and if the EventTarget
is an HTMLElement
. It seems like sidestepping the issue, but I don't think there's another way when the objective requires invoking the browser's native code for the dialog.
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>
A similar pattern will be necessary when invoking any method that requires native code to run (like console.log
or XMLHttpRequest
if fetch
isn't available, or setTimeout
- some things just can't be emulated using your own Javascript, and require invoking some native code of the browser).
Also note that it looks like you're only trying to add custom methods to HTML elements, in which case it might be good to assign trigger
to HTMLElement.prototype
rather than 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>
Thanks to @Kaiido, I now have it working again. I added a getEvent
function to switch on the eventType
and return the correct Event object. The updated code is like so:
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;
}