closure library generated browser events do not have target - google-closure-library

I am trying to use closure library to build a small app. I am trying to use event delegation with following code:
var dom_ = goog.dom.$('targetelementid');
goog.events.listen( dom_, goog.events.EventType.CLICK, function( e ) {
console.log( e );
}, false);
The problem is that when the event is dispatched and handler function is executed the generated object (goog.events.BrowserEvent ) has the target and currentTarget properties set to null. I don't understand why is that.
If I use the regular listener adding (with addEventListener ) the event passed to the handler function has the target set correctly.
Any notes on how to use the event delegation pattern in closure library code when there seem to be no target specified in the event object?

This is not really the case, the event have target and everything else needed at the moment when it is available in the handler, however after all registered handlers in goog.events are exhausted the event object is disposed (i.e. all its properties are deleted/null-ed) and because the modern web browser console implementations show 'live' view of the objects, the object seem to be empty.
Hint: use 'debugger;' notion in the place where you have dounts to stop JavaScript execution and open the stack in the debugger.

try augmenting your code with a scope object (which is what addEventListener does in the background) since the target is irrelevant if "this" is what dispatched the event.
var dom_ = goog.dom.$('targetelementid');
goog.events.listen( dom_, goog.events.EventType.CLICK, function( e ) {
console.log( e );
}, false, this);
Notice that "this" at the end.

Related

React custom hook without boilerplate for closure

I have a component that should listen to some socket events. I register them as follows to avoid spurious register/unregister actions of the event listeners (socketListen returns the destruction of the listener itself, so that is taken care of).
useEffect(() => socketListen('name', newName => setName(newName)), []);
Now as I have more of these kind of listeners, I want to get rid of boilerplate code by writing a custom hook
const useListener = (event, callback) => {
useEffect(() => socketListen(event, callback), [event, callback]);
};
to be then used as
useListener('name', newName => setName(newName));
intending to put focus on the actual action performed and not on the useEffect invokation.
Now the issue is that I hand over a closure (invoking the state setter setName) to my custom hook which implies that the callback in useListener is changed on rerender and therefore again generates spurious listener registering/unregistering. To salvage I could wrap the closure in a useCallback but that would defy my original goal of reducing the code clutter.
I think I can see the functional reason for above problem (and hope to have conveyed that in my writing), but I wonder if there is a convenient way to write a helper function to do what I intended to accomplish with useListener without additional boilerplate code. My actual code uses more complex closures, but I hope that this example suffices to pinpoint my issue.
You should be able to bail out of the requirement for a memoized closure by using a reference:
const useListener = (event, callback) => {
const ref = useRef();
ref.current = callback;
useEffect(() => (
socketListen(event, newName => ref.current(newName))
), [event]);
};
It's important that you write newName => ref.current(newName) instead of just ref.current so that the property access ref.current doesn't occur until the socketListen callback is invoked. That way ref.current is equal to the most recent callback passed to useListener() and isn't referencing the stale closure from the first call to useListener().

reducer goes into a loop when returning a new array created from state

I am using react-redux 5.0.6 and have a reducer with the following code:
export default (state = [], action) => {
switch(action.type) {
case 'ADD_ENGAGEMENT':
let newArr = state.slice();
newArr.push(action.payload);
return newArr;
case 'UPDATE_ENGAGEMENT':
console.info('UPDATE_ENGAGEMENT')
return state.slice();
// return state;
default:
return state;
}}
The issue occurs within the 'UPDATE_ENGAGEMENT' case -- the actual logic has been removed and replaced with the simplest example to demonstrate the problem.
When a new array created from state via state.slice() is returned, a loop is triggered, causing the action to be dispatched until an 'Uncaught RangeError: Maximum call stack size exceeded' error is raised. Screenshot of the browser console during the issue's occurrence
The issue is not limited to 'slice()' and occurs whenever an array containing any element of state is returned e.g. return [state[0]].
When the original state is returned, the issue does not occur.
I am completely baffled by this behavior and cannot fathom how anything in my application could be causing it. Any insight would be immensely appreciated.
To provide some additional context, below is the code involved in the action's being dispatched:
componentWillReceiveProps(newProps) {
let engagementTemplateData = newProps.selectedEngagementTemplate;
let engagements = newProps.engagements;
if (engagementTemplateData && engagementTemplateData.engagementUuid === this.props.uuid) {
let template = engagementTemplateData.template;
this.updateEngagementTemplate(template);
}
}
updateEngagementTemplate(template) {
let url = `/engagements/${this.props.uuid}`;
let requestHelper = new AjaxRequestHelper(url);
let data = {template_uuid: template.uuid};
this.props.updateEngagement({uuid: this.props.uuid, template: template});
// requestHelper.put(data, response => {
// this.props.updateEngagement({uuid: this.props.uuid, template: template});
// });
}
Basically, the function which triggers the action is called in componentWillReceiveProps as a result of another action. However, I am not sure how helpful this information is, since the reducer itself appears to be working properly when responding to the action -- it's just that something strange is happening with the state, which prevents its elements from being returned.
From the sounds of it (and from the react callstack), I imagine the array changing (by reference) in the store is being picked up by a react component props, which in its should/did update logic is calling that action without a guard. This is often a mistake when calling actions or setState from componentDidMount/Update -
It works when the original state is returned as the reference is the same so React does not continue with its update logic, and hence call your code that publishes the action
Consider this pure component that will cause an endless loop with your reducer code...
export interface IMyProps {
myArray: any[],
updateEngagementAction: () => void
}
export class EndlessLoopFromArrayPropComponent extends React.PureComponent<IMyProps> {
// PureComponent compares props by reference only
// so will cause update if this.props.myArray reference has changed in store (ie from slice())
render() {
// blahblah...
}
componentDidUpdate() {
// this will call action after every update
// as every time this action is called it passes in a new reference this.props.myArray to this component
// so react will update this component again, causing the action to be called again
// ... endless loop
this.props.updateEngagementAction()
}
}
Your implementation will differ of course but this will be the principal that is causing it to happen, so you need to add a guard condition in whatever code path leads to your action being called.
For the code above you would need to check an escape condition before sending the action OR implement shouldComponentUpdate or similar to do deeper prop comparison to guard against unnecessary updates, and hence it would not reach that action code in the componentDidUpdate method
EDIT This was written before the react code was added to question. Here I refer to the action being called without guard in componentDidUpdate however the same applies when called in any of the other lifecycle methods triggered by a prop change, in this case componentWillRecieveProps. To be fair it did have a guard already, but never returned false as a more in-depth props check was needed, so caused a loop to occur via willreceive -> true -> action -> reducer -> willreceive -> true ........

How to send message or establish 2 way communication between two XUL Overlay Firefox extensions/add-ons

I have an XUL Overlay Firefox extension, I need to develop a dummy XUL extension that establishes connection with the original extension and sends a set of parameters (message) to the original extension. In short, I have to trigger my original extension with my dummy extension.
Probably the easiest way to do this is to have the original extension listening for a custom event on the base browser window. The dummy extension can then create and dispatch the event with whatever custom data is desired.
Creating and dispatching the event from the dummy:
function sendDataToMainExtension(data) {
if (typeof window === "undefined") {
//If there is no window defined, get the most recent.
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
}
//This assumes that this event is being both sent from
// and received by privileged (main add-on) code.
var event = new CustomEvent('MyExtensionName-From-Dummy', { 'detail': data });
window.dispatchEvent(event);
}
You may need to take the same steps for making sure the data is visible on the receiving end as would be necessary when firing from privileged code to non-privileged code.
Listening for the event in main:
Components.utils.import("resource://gre/modules/Services.jsm");
const Ci = Components.interfaces;
//Listen for the event on all windows as it is unknown on which one
// the event will be sent.
function loadIntoWindow(myWindow) {
myWindow.addEventListener("MyExtensionName-From-Dummy",
receiveMessageFromDummy, false);
}
function unloadFromWindow(myWindow) {
myWindow.removeEventListener("MyExtensionName-From-Dummy",
receiveMessageFromDummy, false);
}
function forEachOpenWindow(fn) {
// Apply a function to all open browser windows
var windows = Services.wm.getEnumerator("navigator:browser");
let windowCount =0;
while (windows.hasMoreElements()) {
windowCount++;
fn(windows.getNext().QueryInterface(Ci.nsIDOMWindow));
}
}
function receiveMessageFromDummy(event) {
var dataFromDummy = event.detail;
//Do whatever was desired with the data.
}
var WindowListener = {
onOpenWindow: function(aWindow)
{
let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
function onWindowLoad()
{
domWindow.removeEventListener("load",onWindowLoad);
if (domWindow.document.documentElement.getAttribute("windowtype")
== "navigator:browser") {
loadIntoWindow(domWindow);
}
}
domWindow.addEventListener("load",onWindowLoad);
},
onCloseWindow: function(xulWindow) { }, // Each window has an unload event handler.
onWindowTitleChange: function(xulWindow, newTitle) { }
};
//Listen for the custom event on all current browser windows.
forEachOpenWindow(loadIntoWindow);
//Listen for the custom event on any new browser window.
Services.wm.addListener(WindowListener);
The data sent should be available as event.detail within the receiveMessageFromDummy() function.
The code above provides one way communication. Two way communication is obtained just duplicating the code to communicate in the other direction with a different custom event. In other words, by having the main extension dispatching a different custom event called something like MyExtensionName-From-Main and having the dummy extension listening for that event. The code is exactly the same as above, but with the event name changed and the function called being receiveMessageFromMain().
Alternately, you could use Window.postMessage(). Doing so sends a "message" event for which you can listen. However, doing so leads to complications which are easier to avoid by using a custom event (e.g. you have to account for the fact that any code (i.e. some other random extension) could be using this event for their own purpose).
Note: The code to loop through windows was originally taken from Converting an old overlay-based Firefox extension into a restartless addon which that author re-wrote as the initial part of How to convert an overlay extension to restartless on MDN. It has been modified multiple times from that code. It may have even earlier versions from other sources.

jQuery prevent all event bubbling

I'm using jQuery with it's widget factory and using custom events to handle events in my application.
This means that all my event binding looks a lot like:
//...in the widget factory code
$(this.element).addClass('customEventClass');
$(this.element).bind('mysite.loadNextPage', $.proxy(this, 'loadNextPage');
and the events are triggered by doing:
$('.customEventClass').trigger('mysite.loadNextPage');
Because the events are directly bound to the elements that need to receive them, I don't need to have these custom events to bubble up through the DOM. I know I can check whether the even has bubbled up or not by doing this in the event handler code:
if (event.target != event.currentTarget) {
event.stopPropagation();
event.preventDefault();
return;
}
But at the moment because most of the elements that are listening for the custom events don't have a handler registered for 'mysite.loadNextPage' there are 51 events generated where only 1 actually does anything. Is there a way to either:
tell jQuery not to bubble these events at all or
Add a default 'stop propagation' handler to all DOM objects that have class 'customEventClass' to stop them from bubbling up an event that they don't have a specific handler for.
Or are there any other good practices for only triggering events on elements that are interesting in those events, rather than having lots of events be triggered for elements that aren't interested in those events.
You can also return false from your event handler function to stop propagation, that's what I normally use:
Returning false from an event handler will automatically call event.stopPropagation() and event.preventDefault(). A false value can also be passed for the handler as a shorthand for function(){ return false; }. So, $( "a.disabled" ).on( "click", false ); attaches an event handler to all links with class "disabled" that prevents them from being followed when they are clicked and also stops the event from bubbling.
See http://api.jquery.com/on/
It looks like there's no good way to do it with jQuery as it is, but it's very easy to write a new function to allow this.
First I wrote a new function to stop the event from bubbling(, and also log the event why not).
function eventWrapper(event){
var logString = 'Event called: ' + event.type + ":" + event.namespace;
if (jQuery.isFunction(this.log) == true) {
this.log(logString);
}
else if (jQuery.isFunction(Logger.log) == true) {
Logger.log(logString);
}
else{
console.log(logString);
}
event.stopPropagation();
}
And now a new function that is added to jQuery.
// A global GUID counter for objects
guidWrapper: 1,
proxyWrapper: function(wrappedFn, fn, context, wrapFn ) {
var args, proxy, tmp;
if ( typeof context === "string" ) {
tmp = fn[ context ];
context = fn;
fn = tmp;
}
// Quick check to determine if target is callable, in the spec
// this throws a TypeError, but we will just return undefined.
if ( !jQuery.isFunction( fn ) ) {
return undefined;
}
// Simulated bind
args = core_slice.call( arguments, 3 );
proxy = function() {
wrappedFn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
};
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
return proxy;
},
And then instead of binding the function like this:
$(this.element).bind('click', $.proxy(this.click, this));
Instead bind it like this.
$(this.element).bind('click', $.proxyWrapper(eventWrapper, this.click, this));
This means that when the event is triggered, the first element that is listening for that event will call event.stopPropagation on the event, and so it won't bubble up to other elements that may also be listening for that event.

NodeJS wait for callback to finish on event emit

I have and application written in NodeJS with Express and am attempting to use EventEmitter to create a kind of plugin architecture with plugins hooking into the main code by listening to emitted events.
My problem comes when a plugin function makes an async request (to get data from mongo in this case) this causes the plugin code to finish and return control back to the original emitter which will then complete execution, before the async request in the plugin code finishes.
E.g:
Main App:
// We want to modify the request object in the plugin
self.emit('plugin-listener', request);
Plugin:
// Plugin function listening to 'plugin-listener', 'request' is an arg
console.log(request);
// Call to DB (async)
this.getFromMongo(some_data, function(response){
// this may not get called until the plugin function has finished!
}
My reason for avoiding a callback function back to the main code from the 'getFromMongo' function is that there may be 0 or many plugins listening to the event. Ideally I want some way to wait for the DB stuff to finish before returning control to the main app
Many Thanks
Using the EventEmitter for plugin/middleware management is not ideal, because you cannot ensure that the listeners are executed sequentially, if they have asynchroneous code. This especially is a problem when these listeners interact with each other or the same data.
That's why i.e. connect/express middleware functions are stored in an array and executed one after the other, instead of using an EventEmitter; They each need to call a next(); function when they are done doing their task.
You can't mix asynchronous calls with synchronous behavior. If you're going to stick with event emitter (which may not be ideal for you as Klovadis pointed out), you'll need to have your plugin emit an event that triggers a function in the main app which contains the code that you want to 'wait' to execute. You would also have to in turn keep track of all the plugin calls you made that you are waiting for event calls for so that your main code doesn't run until all the plugin calls have finished their MongoDB callbacks.
var callList = ['pluginArgs1', 'pluginArgs2', 'pluginArgs3'];
for (var i = 0; i < callList.length; i++){
self.emit('plugin-listener', callList[i], i);
}
self.on('plugin-callback', function(i){
callList.splice(i, 1);
if (callList.length < 1){
//we're done, do something
}
});
Had the same kind of decision to make about some events that I sometime need to wait for before returning the response to the client and sometimes not (when not in an HTTP request context).
The easiest way for me was to add a callback as the last argument of the event.
Stuff.emit('do_some_stuff', data, data2, callback);
In the event check if there is a callback:
Stuff.on('do_some_stuff', function(data, data2, callback) {
// stuff to do
// ...
if (typeof callback === "function") return callback(err, result);
});
I know that mixing event and callbacks can be messy but that work fine for what I need.
The other solution I see is the one proposed by #redben: add an emit function at the end of the event. The problem when in a HTTP context is that you need unique keys so your events don't mess up if they do different stuff per user.
Haven't tried it myself but you could use a property in the event's data object as an array of functions to execute by the code that emitted the event :
Listeners
foo.on('your-event', function(data) {
console.log(data);
// Then add the asynchronous code to a callbacks array
// in the event data object
data.callbacks.push(function(next) {
getFromMongo(some_data, function(err, result) { next(err) }
}
});
Emitter
self.emit('your-event', data);
// listeners have modified data object,
// some might have added callback to data.callbacks
// (suppose you use async)
async.series(data.callbacks);
This seems quite dangerous, but I have to do it anyway...
const ee = new EventEmitter();
if (ee.listeners("async-event").length > 0) {
await new Promise((resolve) => {
ee.emit("async-event", data1, data2, resolve);
});
}
Otherwise, just emit the event back-and-forth.

Resources