Ich suche nach einer einfachen und abstrakten Möglichkeit, nur DOM-Ereignisse zu klonen oder erneut zu versenden. Ich bin nicht daran interessiert, DOM-Knoten zu klonen.
Ich habe ein bisschen experimentiert, die DOM-Ereignisspezifikation gelesen und keine klare Antwort gefunden.
Im Idealfall suche ich etwas so Unkompliziertes wie:
handler = function(e){
document.getElementById("decoy").dispatchEvent(e)
}
document.getElementById("source").addEventListener("click", handler)
Dieses Codebeispiel funktioniert natürlich nicht. Es gibt eine DOM-Ausnahme, die besagt, dass das Ereignis derzeit ausgelöst wird - offensichtlich.
Ich möchte vermeiden, neue Ereignisse manuell mit zu erstellen document.createEvent()
, zu initialisieren und zu versenden.
Gibt es eine einfache Lösung für diesen Anwendungsfall?
javascript
events
dom
webkit
Razvan Caliman
quelle
quelle
Antworten:
Ich weiß, die Frage ist alt und das OP wollte vermeiden, einen Ansatz zu erstellen / zu initialisieren, aber es gibt eine relativ einfache Möglichkeit, Ereignisse zu duplizieren:
new_event = new MouseEvent(old_event.type, old_event)
Wenn Sie mehr als nur Mausereignisse möchten, können Sie Folgendes tun:
new_event = new old_event.constructor(old_event.type, old_event)
Und im ursprünglichen Kontext:
handler = function(e) { new_e = new e.constructor(e.type, e); document.getElementById("decoy").dispatchEvent(new_e); } document.getElementById("source").addEventListener("click", handler);
(Für jQuery-Benutzer: Möglicherweise müssen Sie
e.originalEvent.constructor
statt verwendene.constructor
)quelle
Uncaught TypeError: Illegal constructor
:(Ein Fix für Internet Explorer
Alexis veröffentlicht eine nette Lösung, aber seine Lösung funktioniert nicht im Internet Explorer. Die folgende Lösung wird. Leider gibt es in Internet Explorer kein System, das so konsistent ist wie Ereigniskonstruktoren. Daher ist der unten aufgeblähte Code erforderlich.
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>
Eine allgemeinere Lösung
Abhängig von Ihren Anforderungen ist die Weitergabe synthetischer Ereignisse möglicherweise eine viel bessere Lösung als das erneute Versenden des ursprünglichen Ereignisses. Wir erstellen spezielle Möglichkeiten zum Registrieren von Ereignis-Listenern, die diese Listener auch unserem Code aussetzen, damit wir sie manuell aufrufen können. In der Tat gibt es eine
getEventListeners
Funktion, mit der aktuelle Ereignis-Listener abgerufen werden können. WirdgetEventListeners
jedoch nur von Chrome / Safari unterstützt. Daher habe ich den folgenden Ersatz entworfen. Obwohl der folgende Code viel zu groß aussieht, handelt es sich bei dem folgenden Code hauptsächlich um Variablennamen, sodass er nach der Minimierung sehr klein ist./**@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>
Beachten Sie, dass nach der Minimierung der gesamte Code genau in ein einzelnes Kilobyte passt (vor 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>
quelle
if (eventCopy.defaultPrevented) original.preventDefault();
?