I've set up an event listener like this...
window.addEventListener('message', parseMessage, false);
var parseMessage = function(rawMessage) {
console.log(rawMessage.cmd);
};
And then I'm triggering the event like this:
var event = new Event('message', {'cmd':"blerg!"});
window.dispatchEvent(event);
The problem is the console.log in parse message is logging out undefined when I am expecting to to log out "blerg!"
What I am I doing wrong here with the events, how to I pass the 'cmd' message through to the event?
Use CustomEvent instead of Event for creating custom events.
Specify your data in a 'details' object (see code).
I changed the event name because message is also used for the postMessage API. It didn't cause problems when running in Chrome, but I wouldn't use it.
var parseMessage = function(rawMessage) {
console.log(rawMessage);
console.log(rawMessage.detail.cmd);
};
// changed event name
window.addEventListener('myMessage', parseMessage, false);
// data should be in a 'details' object
var evt = new CustomEvent('myMessage', {
detail: {
'cmd' : "blerg!"
}
});
window.dispatchEvent(evt);
Here is an adjustment for IE >= 9 compatiblity (using document.createEvent() and CustomEvent::initCustomEvent()):
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent('myMessage', false, false, {
'cmd': "blerg!"
});
For IE9/10 polyfill you can use this code provided by Mozilla:
https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
(function () {
if (
typeof window.CustomEvent === "function" ||
// In Safari, typeof CustomEvent == 'object' but it otherwise works fine
this.CustomEvent.toString().indexOf('CustomEventConstructor')>-1
) { return; }
function CustomEvent ( event, params ) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
Also described here but with wrong URL:
https://stackoverflow.com/a/22946340/1736012
Related
I am trying to dispatch events programatically for keyboard and mouse events.
So far there are few things I have tried. Right now I am able to dispatch mouse events programatically and see the changes:
const element = document.getElementById("element");
const event = new Event('click', {bubbles: true});
element.dispatchEvent(event);
Above method is working fine for Mouse Event. And I have tried following method for keyboard events:
const element = document.getElementById("input-element");
const event = new KeyboardEvent('keypress', {'key': 'e'});
element.dispatchEvent(event);
It seems here that the event is being executed, but the values are not being updated in the input field.
There are a number of events that occur when you press a key. None of them result in an input value being changed, but you can use this function to approximate what happens when a key is pressed.
const element = document.getElementById("input-element");
const key = 'e';
var success = triggerKey(key, element);
console.log(success?'success':'fail');
function triggerKey(key, element){
if(!/^[a-z]$/.test(key)) return false;
if(!['INPUT','TEXTAREA'].includes(element.tagName)) return false;
const events = ['keydown', 'keypress', 'textInput', 'keyup'];
events.forEach(event_name=>{
const opts = 'textInput' === event_name ? {
inputType: 'insertText',
data: key
} : {
key: key,
code: `Key${key.toUpperCase()}`
};
const event = 'textInput' === event_name ?
new InputEvent('input', opts) :
new KeyboardEvent(event_name, opts);
element.dispatchEvent(event);
if('textInput' === event_name) element.value += key;
});
return true;
}
<input id="input-element">
I'm trying to find out how to handle custom DOM events emitted by something outside of Angular, for example the following:
document.querySelector('my-custom-element').dispatchEvent(new Event('my.customEvent'));
So far I have tried to register a new EventManagerPlugin that supports everything starting with 'my.' but if I print out all events that come by all 'normal' event like 'click' and 'submit' are printed out; but none of my custom events.
html:
<my-custom-element (my.customEvent)="handleCustomEvent($event)"></my-custom-element>
ts:
supports(eventName: string):boolean {
var ret = false;
if (eventName.indexOf('my.') === 0) {
ret = true;
}
console.log('supports event?', eventName, ret);
return ret;
}
The console.log line only prints native events and ng*events but not my custom event :(
EDIT Fixed solution
I've moved the (my.customEvent) inside the component annd the log showed the custom event.
Binding an external event to the angular2 internal event while seperating the 2 is fixed by using a custom eventHandler in the EventManagerPlugin
Relevate code
addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
let zone = this.manager.getZone();
// Entering back into angular to trigger changeDetection
var outsideHandler = (event: any) => {
zone.run(() => handler(event));
};
// Executed outside of angular so that change detection is not constantly triggered.
var addAndRemoveHostListenersForOutsideEvents = () => {
this.manager.addEventListener(element, 'external.' + eventName, outsideHandler);
}
return this.manager.getZone().runOutsideAngular(addAndRemoveHostListenersForOutsideEvents);
}
Trigger the event via DOM:
document.querySelector('my-custom-element').dispatchEvent(new Event('external.my.customEvent'));
Now you can trigger an event from the DOM which is pushed into angular2 world and can the code is handled from within the component.
Try to extend the DomEventsPlugin, for example:
import {DomEventsPlugin} from 'angular2/platform/common_dom';
// Have to pull DOM from src because platform/common_dom returns DOM as null.
// I believe its a TS bug.
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {Injectable} from 'angular2/core';
import {noop} from 'angular2/src/facade/lang';
#Injectable()
export class DOMOutsideEventPlugin extends DomEventsPlugin {
eventMap: Object = {
"clickOutside": "click",
"mousedownOutside": "mousedown",
"mouseupOutside": "mouseup",
"mousemoveOutside": "mousemove"
}
supports(eventName: string): boolean {
return this.eventMap.hasOwnProperty(eventName);
}
addEventListener(element: HTMLElement, eventName: string, handler: Function): Function {
var zone = this.manager.getZone();
var documentEvent = this.eventMap[eventName];
// Entering back into angular to trigger changeDetection
var outsideHandler = (event) => {
zone.run(() => handler(event))
};
// Executed outside of angular so that change detection is not constantly triggered.
var addAndRemoveHostListenersForOutsideEvents = () => {
DOM.onAndCancel(DOM.getGlobalEventTarget('document'), documentEvent,
(event) => {
let current = event.target;
// if the element/event is propagating from the element its bound to, don't handle it.
if (current.parentNode && current !== element) {
outsideHandler(event);
}
});
}
return this.manager.getZone().runOutsideAngular(addAndRemoveHostListenersForOutsideEvents);
}
addGlobalEventListener(target: string, eventName: string, handler: Function): Function {
var element = DOM.getGlobalEventTarget(target);
var zone = this.manager.getZone();
var outsideHandler = (event) => zone.run(() => handler(event));
if ((target === "document") || (target === "window" )) {
return noop;
}
return this.manager.getZone().runOutsideAngular(
() => DOM.onAndCancel(element, eventName, outsideHandler)
);
}
}
source: https://medium.com/#TheLarkInn/creating-custom-dom-events-in-angular2-f326d348dc8b#.so0jvssnz
I have a shimmed and polyfilled version of angularjs 1.3 working perfectly on ie8. Unfortunately when mootools is included on the page there are quite a few conflicts. I have managed to get a handle on all but one with the following which adds add / remove EventListener and dispatchEvent to Window.prototype, HTMLDocument.prototype and Element.prototype. It checks to see if mootools is loaded and if so it adds them differently.
!window.addEventListener && (function (WindowPrototype, DocumentPrototype, ElementPrototype, addEventListener, removeEventListener, dispatchEvent, registry) {
var addEventListenerFn = function(type, listener) {
var target = this;
registry.unshift([target, type, listener,
function (event) {
event.currentTarget = target;
event.preventDefault = function () {
event.returnValue = false;
};
event.stopPropagation = function () {
event.cancelBubble = true;
};
event.target = event.srcElement || target;
listener.call(target, event);
}]);
// http://msdn.microsoft.com/en-us/library/ie/hh180173%28v=vs.85%29.aspx
if (type === 'load' && this.tagName && this.tagName === 'SCRIPT') {
var reg = registry[0][3];
this.onreadystatechange = function (event) {
if (this.readyState === "loaded" || this.readyState === "complete") {
reg.call(this, {
type: "load"
});
}
}
} else {
this.attachEvent('on' + type, registry[0][3]);
}
};
var removeEventListenerFn = function(type, listener) {
for (var index = 0, register; register = registry[index]; ++index) {
if (register[0] == this && register[1] == type && register[2] == listener) {
if (type === 'load' && this.tagName && this.tagName === 'SCRIPT') {
this.onreadystatechange = null;
}
return this.detachEvent('on' + type, registry.splice(index, 1)[0][3]);
}
}
};
var dispatchEventFn = function(eventObject) {
return this.fireEvent('on' + eventObject.type, eventObject);
};
if(Element.prototype.$constructor && typeof Element.prototype.$constructor === 'function') {
Element.implement(addEventListener, addEventListenerFn);
Element.implement(removeEventListener, removeEventListenerFn);
Element.implement(dispatchEvent, dispatchEventFn);
Window.implement(addEventListener, addEventListenerFn);
Window.implement(removeEventListener, removeEventListenerFn);
Window.implement(dispatchEvent, dispatchEventFn);
} else {
WindowPrototype[addEventListener] = ElementPrototype[addEventListener] = addEventListenerFn;
WindowPrototype[removeEventListener] = ElementPrototype[removeEventListener] = removeEventListenerFn;
WindowPrototype[dispatchEvent] = ElementPrototype[dispatchEvent] = dispatchEventFn;
}
DocumentPrototype[addEventListener] = addEventListenerFn;
DocumentPrototype[removeEventListener] = removeEventListenerFn;
DocumentPrototype[dispatchEvent] = dispatchEventFn;
})(Window.prototype, HTMLDocument.prototype, Element.prototype, 'addEventListener', 'removeEventListener', 'dispatchEvent', []);
This has resolved all my errors bar one. When this function is called in Angular, when mootools is on the page, and element is a form addEventListener is undefined.
addEventListenerFn = function(element, type, fn) {
element.addEventListener(type, fn, false);
}
specifically this function is called from angulars formDirective like so
addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
Any ideas why the form element still dosn't have the addEventListener function available?
Extending native type via Element.prototype in IE8 is considered very unreliable as the prototype is only partially exposed and certain things are not inheriting from it / misbehave.
http://perfectionkills.com/whats-wrong-with-extending-the-dom/
What MooTools does in this case is rather than work around the quirks of all edgecases that don't adhere to the correct proto chain (and because of IE6/7 before that) is to COPY the Element prototypes on the objects of the DOM nodes as you pass it through the $ selector.
This is not ideal, because.
var foo = document.id('foo');
// all known methods from Element.prototype are copied on foo, which now hasOwnProperty for them
Element.prototype.bar = function(){};
foo.bar(); // no own property bar, going up the chain may fail dependent on nodeType
Anyway, that aside - you can fix your particular problem by copying your methods from the Element.prototype to the special HTMLFormElement.prototype - any any other elements constructors you may find to differ.
It's not scalable, you may then get an error in say HTMLInputElement and so forth, where do you draw the line?
I need to found other way how to use javascript function.
var b = ce("input"); // Here I create element.
b.setAttribute("name", "g4");
b.value = "Centimetrais(pvz:187.5)";
b.onfocus = function() { remv(this); };
b.onchange = function() { abs(this); };
b.onkeypress = function() { on(event); }; // I need to change this place becose then I pass "event" argument function doesn't work.
ac(1, "", b); // Here I appendChild element in form.
Here is the function:
function on(evt) {
var theEvent = evt|| window.event;
var key = theEvent.keyCode || theEvent.which;
key = String.fromCharCode( key );
var regex = /^[0-9.,]+$/;
if( !regex.test(key) ) {
theEvent.returnValue = false;
if(theEvent.preventDefault) theEvent.preventDefault();
}
}
In IE and chrome it work but in mozilla doesn't. Any alternative how to fix it for firefox?
Also at this path other function working in mozilla if pass other argument like "car","dog",this. For example:
firstFunction();
function firstFunction() {
var b = ce("input"); // Here I create element.
b.onkeypress = function() { on("hi!"); };
ac(1, "", b); // Here I appendChild element in form.
}
function on(evt) {
alert(evt);
}
If I understand your question correctly, the problem is that you're not accepting the event argument in the main handler. E.g., in your first example:
b.onkeypress = function() { on(event); };
it should be
b.onkeypress = function(e) { on(e || window.event); };
// Changes here --------^ and --^^^^^^^^^^^^
You're doing that in on, but in on, it's already too late, you've lost the argument provided to the onXYZ function.
The reason your code works in Chrome and IE is that IE uses a global event object instead of (or in modern versions, in addition to) the one it actually passes into the handler, and Chrome replicates that behavior for maximum compatibility with websites that expect that. Firefox does not.
I am working on a Firefox extension, and I am trying to pass a parameter to addEventListener. I am "listening" for changes in the page title, my code looks something like this:
function Test()
{
this.checkTitle = function( event )
{
var func = function() { this.onTitleChange ( event ); };
var target = content.document.getElementsByTagName('TITLE')[0];
target.addEventListener('DOMSubtreeModified', func, false);
}
this.onTitleChange = function( e )
{
// do stuff with e
alert('test');
}
this.handleEvent = function (event)
{
switch (event.type)
{
case "DOMContentLoaded":
{
this.checkTitle( event );
}
}
}
window.addEventListener ("DOMContentLoaded", this, false);
}
I never get the 'test' alert, if I use func = function() { alert(event); }; it does show an alert with '[object Event]'. Also tried without using this. on func but still wont work.
How can I make this work to be able to access checkTitle parameter "event" into onTitleChange?
When the browser calls the event handler, this will refer to the element, not your instance.
You need to save a copy of the desired this in a separate variable:
var self = this;
var func = function(e) { self.onTitleChange (e); };
var target = content.document.getElementsByTagName('TITLE')[0];
target.addEventListener('DOMSubtreeModified', func, false);