Une solution pour Internet Explorer
Alexis propose une solution intéressante, mais elle ne fonctionne pas avec Internet Explorer. La solution ci-dessous fonctionnera. Malheureusement, il n'existe pas de système aussi cohérent que les constructeurs d'événements dans Internet Explorer, et le gonflement du code ci-dessous est donc nécessaire.
var allModifiers = ["Alt","AltGraph","CapsLock","Control",
"Meta","NumLock","Scroll","Shift","Win"];
function redispatchEvent(original, newTargetId) {
if (typeof Event === "function") {
var eventCopy = new original.constructor(original.type, original);
} else {
// Internet Explorer
var eventType = original.constructor.name;
var eventCopy = document.createEvent(eventType);
if (original.getModifierState)
var modifiersList = allModifiers.filter(
original.getModifierState,
original
).join(" ");
if (eventType === "MouseEvent") original.initMouseEvent(
original.type, original.bubbles, original.cancelable,
original.view, original.detail, original.screenX, original.screenY,
original.clientX, original.clientY, original.ctrlKey,
original.altKey, original.shiftKey, original.metaKey,
original.button, original.relatedTarget
);
if (eventType === "DragEvent") original.initDragEvent(
original.type, original.bubbles, original.cancelable,
original.view, original.detail, original.screenX, original.screenY,
original.clientX, original.clientY, original.ctrlKey,
original.altKey, original.shiftKey, original.metaKey,
original.button, original.relatedTarget, original.dataTransfer
);
if (eventType === "WheelEvent") original.initWheelEvent(
original.detail, original.screenX, original.screenY,
original.clientX, original.clientY, original.button,
original.relatedTarget, modifiersList,
original.deltaX, original.deltaY, original.deltaZ, original.deltaMode
);
if (eventType === "PointerEvent") original.initPointerEvent(
original.type, original.bubbles, original.cancelable,
original.view, original.detail, original.screenX, original.screenY,
original.clientX, original.clientY, original.ctrlKey,
original.altKey, original.shiftKey, original.metaKey,
original.button, original.relatedTarget,
original.offsetX, original.offsetY, original.width, original.height,
original.pressure, original.rotation,
original.tiltX, original.tiltY,
original.pointerId, original.pointerType,
original.timeStamp, original.isPrimary
);
if (eventType === "TouchEvent") original.initTouchEvent(
original.type, original.bubbles, original.cancelable,
original.view, original.detail, original.screenX, original.screenY,
original.clientX, original.clientY, original.ctrlKey,
original.altKey, original.shiftKey, original.metaKey,
original.touches, original.targetTouches, original.changedTouches,
original.scale, original.rotation
);
if (eventType === "TextEvent") original.initTextEvent(
original.type, original.bubbles, original.cancelable,
original.view,
original.data, original.inputMethod, original.locale
);
if (eventType === "CompositionEvent") original.initTextEvent(
original.type, original.bubbles, original.cancelable,
original.view,
original.data, original.inputMethod, original.locale
);
if (eventType === "KeyboardEvent") original.initKeyboardEvent(
original.type, original.bubbles, original.cancelable,
original.view, original.char, original.key,
original.location, modifiersList, original.repeat
);
if (eventType === "InputEvent" || eventType === "UIEvent")
original.initUIEvent(
original.type, original.bubbles, original.cancelable,
original.view, original.detail
);
if (eventType === "FocusEvent") original.initFocusEvent(
original.type, original.bubbles, original.cancelable,
original.view, original.detail, original.relatedTarget
);
}
document.getElementById(newTargetId).dispatchEvent(eventCopy);
if (eventCopy.defaultPrevented) newTargetId.preventDefault();
}
<button onclick="redispatchEvent(arguments[0], '2nd')">Click Here</button>
<button id="2nd" onclick="console.log('Alternate clicked!')">Alternate Button</button>
Une solution plus générale
En fonction de vos besoins, une solution bien meilleure que la réexpédition de l'événement original pourrait être la propagation synthétique d'événements. Nous créons des moyens spéciaux pour enregistrer des écouteurs d'événements qui exposent également ces écouteurs à notre code afin que nous puissions les appeler manuellement. En effet, il existe un getEventListeners
qui peut être utilisée pour récupérer les auditeurs d'événements actuels. Cependant, getEventListeners
n'est pris en charge que par Chrome/Safari. J'ai donc conçu le remplacement suivant. Bien que le code ci-dessous semble beaucoup trop gros, il s'agit principalement de noms de variables, donc il sera très petit après la minification.
/**@type{WeakMap}*/ var registeredListeners = new WeakMap();
hearEvent(document.getElementById("1st"), "click", function propagate(evt) {
fireEvent(document.getElementById("2nd"), evt, propagate);
});
hearEvent(document.getElementById("2nd"), "click", function(evt) {
console.log( evt.target.textContent );
});
/**
* @param{Element} target
* @param{string} name
* @param{function(Event=):(boolean|undefined)} handle
* @param{(Object<string,boolean>|boolean)=} options
* @return {undefined}
*/
function hearEvent(target, name, handle, options) {
target.addEventListener(name, handle, options);
var curArr = registeredListeners.get(target);
if (!curArr) registeredListeners.set(target, (curArr = []));
curArr.push([
"" + name,
handle,
typeof options=="object" ? !!options.capture : !!options,
target
]);
}
/**
* @param{Element} target
* @param{string} name
* @param{function(Event=):(boolean|undefined)} handle
* @param{(Object<string,boolean>|boolean)=} options
* @return {undefined}
*/
function muteEvent(target, name, handle, options) {
name += "";
target.removeEventListener(name, handle, options);
var capturing = typeof options=="object"?!!options.capture:!!options;
var curArr = registeredListeners.get(target);
if (curArr)
for (var i=(curArr.length|0)-1|0; i>=0; i=i-1|0)
if (curArr[i][0] === name && curArr[i][2] === capturing)
curArr.splice(i, 1);
if (!curArr.length) registeredListeners.delete(target);
}
/**
* @param{Element} target
* @param{Event} eventObject
* @param{Element=} caller
* @return {undefined}
*/
function fireEvent(target, eventObject, caller) {
var deffered = [], name = eventObject.type, curArr, listener;
var immediateStop = false, keepGoing = true, lastTarget;
var currentTarget = target, doesBubble = !!eventObject.bubbles;
var trueObject = Object.setPrototypeOf({
stopImmediatePropagation: function(){immediateStop = true},
stopPropagation: function(){keepGoing = false},
get target() {return target},
get currentTarget() {return currentTarget}
}, eventObject);
do {
if (curArr = registeredListeners.get(currentTarget))
for (var i=0; i<(curArr.length|0) && !immediateStop; i=i+1|0)
if (curArr[i][0] === name && curArr[i][1] !== caller) {
listener = curArr[i];
if (listener[2]) {
listener[1].call(trueObject, trueObject);
} else if (doesBubble || currentTarget === target) {
deffered.push( listener );
}
}
if (target.nodeType === 13) {
// for the ShadowDOMv2
deffered.push([ target ]);
currentTarget = target = currentTarget.host;
}
} while (keepGoing && (currentTarget = currentTarget.parentNode));
while (
(listener = deffered.pop()) &&
!immediateStop &&
(lastTarget === listener[3] || keepGoing)
)
if (listener.length === 1) {
// for the ShadowDOMv2
target = listener[0];
} else {
lastTarget = currentTarget = listener[3];
listener[1].call(trueObject, trueObject);
}
}
<button id="1st">Click Here</button>
<button id="2nd">Alternate Button</button>
Observez que, après réduction, tout ce code tient parfaitement dans un seul kilooctet (avant gzip).
var k=new WeakMap;m(document.getElementById("1st"),"click",function q(a){r(document.getElementById("2nd"),a,q)});m(document.getElementById("2nd"),"click",function(a){console.log(a.target.textContent)});function m(a,c,f,b){a.addEventListener(c,f,b);var d=k.get(a);d||k.set(a,d=[]);d.push([""+c,f,"object"==typeof b?!!b.capture:!!b,a])}
function r(a,c,f){var b=[],d=c.type,n=!1,p=!0,g=a,t=!!c.bubbles,l=Object.setPrototypeOf({stopImmediatePropagation:function(){n=!0},stopPropagation:function(){p=!1},get target(){return a},get currentTarget(){return g}},c);do{if(c=k.get(g))for(var h=0;h<(c.length|0)&&!n;h=h+1|0)if(c[h][0]===d&&c[h][1]!==f){var e=c[h];e[2]?e[1].call(l,l):(t||g===a)&&b.push(e)}13===a.nodeType&&(b.push([a]),g=a=g.host)}while(p&&(g=g.parentNode));for(;(e=b.pop())&&!n&&(u===e[3]||p);)if(1===e.length)a=e[0];else{var u=g=
e[3];e[1].call(l,l)}}function z(a,c,f,b){c+="";a.removeEventListener(c,f,b);f="object"==typeof b?!!b.capture:!!b;if(b=k.get(a))for(var d=(b.length|0)-1|0;0<=d;d=d-1|0)b[d][0]===c&&b[d][2]===f&&b.splice(d,1);b.length||k.delete(a)}
<button id="1st">Click Here</button>
<button id="2nd">Alternate Button</button>