I have this function in my codebase:
let touch = true;
function init() {
let firstMousemoveHasOccured = false;
$(window).on('mousemove.touchdetection', () => {
if (firstMousemoveHasOccured) {
touch = false;
$(window).off('mousemove.touchdetection touchend.touchdetection');
} else {
firstMousemoveHasOccured = true;
}
});
$(window).on('touchend.touchdetection', () => {
touch = true;
$(window).off('mousemove.touchdetection touchend.touchdetection');
});
}
The event mousemove.touchdetection is not a standard event, so where is this coming from?
These are jQuery namespaced events
The first part of the event name mousemove is the event that, when fired calls the callback. The second part touchdetection is meaningless, except it allows you a mechanism to turn off a specific class or group of mousemove events, easily.
$(document).off('mousemove'); //turns off all callbacks attached to the `mousemove` event.
$(document).off('mousemove.touchdetection'); //turns of all callbacks attached to the mousemove event that have been attached with the touchdetection namespace
The purpose of this, as you'll see from reading the API docs is to allow you to easily modify your listeners in your application without affecting listeners attached by third party code.
Related
I have an addEventListener built if there is a real-time change in my database:
db.collection(xx).doc(yy).onSnapshot(user => {
accept_button.addEventListener("click", function() {
initializeChat();
})
The .onSnapshot is a real-time listener to my database, in another word, if there is a change in my database of document yy, the accept_button appears, then the initializeChat function is built into accept_button.
When the document is changed x times, the addEventListener function is built x times, and initializeChat is executed x times. How do I make such that the same addEventListener function overwrites one another, and initializeChat only executes once?
Thanks for your time and help.
Use onclick instead of addEventListener.
This will remove multiple event handling.
db.collection(xx).doc(yy).onSnapshot(user => {
accept_button.onclick = function() {
initializeChat();
}
})
It sounds like you just need to check a flag to see if a listener has been added to the button yet:
let listenerAdded = false;
db.collection(xx).doc(yy).onSnapshot(user => {
if (listenerAdded) {
return;
}
listenerAdded = true;
accept_button.addEventListener("click", initializeChat);
})
Note that there's no need for an extra anonymous function for the click callback.
It's usually not a good idea (because only one onclick listener can be assigned at a time), but if you're sure no other click listeners will be added to the button, you could also assign to the onclick and check it instead:
db.collection(xx).doc(yy).onSnapshot(user => {
if (accept_button.onclick) {
return;
}
accept_button.onclick = initializeChat;
})
I was confused when reading code of stream.Readable in Node.js.
here is source code:
https://github.com/nodejs/node/blob/master/lib/_stream_readable.js#L778-L799
Readable.prototype.on = function(ev, fn) {
const res = Stream.prototype.on.call(this, ev, fn);
if (ev === 'data') {
// Start flowing on next tick if stream isn't explicitly paused
if (this._readableState.flowing !== false)
this.resume();
} else if (ev === 'readable') {
const state = this._readableState;
if (!state.endEmitted && !state.readableListening) {
state.readableListening = state.needReadable = true;
state.emittedReadable = false;
if (!state.reading) {
process.nextTick(nReadingNextTick, this);
} else if (state.length) {
emitReadable(this);
}
}
}
return res;
};
Obviously, the if statements only handle data and Readable event, but according to the API document, the on method of stream.readable also accept other events such as close , end , error.
So my question is :
According to the source code, how did stream.Readable handle other events except data and readable?
What you are seeing here is an override for the .on() method so that the Readable class can watch what event listeners are being attached and can do something special when someone installs a listener for the data event or the readable event.
The first line of this function:
const res = Stream.prototype.on.call(this, ev, fn);
is where the Readable passes the callback and event name arguments to its parent so that the normal implementation will be run. A Stream implements the EventEmitter interface so calling the super method with Stream.prototype.on.call(this, ev, fn) will give .on() it's expected default behavior.
Then after calling the parent, it checks to see if the event that someone is listening to is the data event or readable event and then implements a little extra functionality when one of those event listeners is attached.
For the data event, it resumes the stream so that it will start flowing if it was paused and if it was set to flowing mode. This is probably because when a Readable is being initially created and configured, if it starts flowing the stream before the data event listener is attached, then data on the stream could be missed. So, it doesn't start flowing until someone is around to listen to data events.
Note, there are potentially lots of over events that occur on the stream and those are all handled by the call to the base class in the first line. What you are seeing here is just some special behavior that the Readable class wants to implement when two specific event listeners are first added. This code does not affect when those events are sent or how they are listened to. It just triggers a little behavior in the Readable state when a listener for one of these events is first attached.
I am using kendo schedule widget and want to prevent datsource from reading after crud operations under certain circumstances.
I tryed this by attaching to the requestStart event:
function subscribeToEvent(e) {
if (e.condition===condition) {
var scheduler = $("#scheduleCustomerSchedule").data("kendoScheduler");
scheduler.dataSource.bind("requestStart", dataSource_requestStart);
}
}
function dataSource_requestStart(e) {
e.preventDefault();
}
This works but the probem is, that I dont know how die unbind this event after it has been executed.
In my case this code prevents dataSource.Read() forever, of course.
thx
You need to subscribe to the requestStart event and check for the condition in the actual event handler. If that specific condition is only available in the subscribeToEvent method, you can pass it using a closure.
function subscribeToEvent(e) {
var scheduler = $("#scheduleCustomerSchedule").data("kendoScheduler");
scheduler.dataSource.bind("requestStart", dataSource_requestStart);
}
function dataSource_requestStart(e) {
if (e.condition === condition)
e.preventDefault();
}
I'm writing a Chrome extension. It's used for recording users' behavior on browsing web pages. It does that by adding event listeners to customers' web pages, using Chrome content script.
Code in content script looks like:
var recordingEvents = ['click', 'input', 'change'];
recordingEvents.forEach(function (e) {
window.addEventListener(e, handler, true);
});
Example of custom page:
<script>
function reload() {
var ifrw = document.getElementById("iframeResult").contentWindow;
ifrw.document.open();
ifrw.document.write("<div>abc</div>");
ifrw.document.close();
}
</script>
<body>
<input type="submit" onclick="reload();" value="Reload" />
<iframe id="iframeResult"></iframe>
</body>
It uses document.open, document.write to rewrite content of iframe.
Here is the question. My event listeners are attached to window object. And document.open removes all its event listeners. Like picture below shows.
Is there a way to avoid document.open removing event listeners? Or to observe document.open, so I can manually re-add listeners after it?
I've found this issue trying to solve exactly the same problem.
Here is a spec https://html.spec.whatwg.org/multipage/webappapis.html#dom-document-open that says that on document.open current document is destroyed and replaced with a fresh one. I had a hope that some event's like "load" are still preserved, no luck.
Here is my detection code:
const testEventName = 'TestEvent';
let tm;
function onTestEvent() {
clearTimeout(tm);
}
function listenToTestEvent() {
document.addEventListener(testEventName, onTestEvent);
}
listenToTestEvent();
function onLostEvents() {
console.log('events are lost');
listenToTestEvent();
// DO THING HERE
}
function checkIfEventsAreLost() {
document.dispatchEvent(new CustomEvent(testEventName));
tm = setTimeout(onLostEvents);
}
new MutationObserver(checkIfEventsAreLost).observe(document, { childList: true });
When document is recreated its childList is changed(new documentElementnode), this is the best trigger I've thought of to detect document replacement.
Note that even listeners fire before setTimeout(..., 0)
This is a detailed explanation of why #Viller's answer works. I'm making this a new answer as it didn't fit into a comment
The TestEvent event is a special event that monitors when the events that were previously setup in a document are removed.
In particular, this accounts for the case of document.open, which removes all listeners not only from the document but also from the window.
The general idea is to setup a listener for a custom event called TestEvent, which clears a timeout. Such timeout is setup only when the document mutates and is triggered by a mutation observer.
Since the timeout schedules the operation to happen at least during the next tick of the event loop, such timeout can be cleared before that, avoiding the execution of its callback all together. And, since the TestEvent event handler clears that timeout, the fact that the timeout is cleared implies that the listener is still attached. On the other hand, if the timeout is not cleared before the next tick, the would signify the events were removed and a new "setup" is needed.
According to MDN:
The Document.open() method [...] come(s) with some side effects. For example:
All event listeners currently registered on the document, nodes inside
the document, or the document's window are removed.
Below I provide a module (onGlobalListenerRemoval) where one can easily register some callback functions to get notified whenever listeners get cleared. This uses the same working principle as the code in Viller's answer.
Usage principle:
onGlobalListenerRemoval.addListener(() => {
alert("All event listeners got removed!")
});
Module code:
const onGlobalListenerRemoval = (() => {
const callbacks = new Set();
const eventName = "listenerStillAttached";
window.addEventListener(eventName, _handleListenerStillAttached);
new MutationObserver((entries) => {
const documentReplaced = entries.some(entry =>
Array.from(entry.addedNodes).includes(document.documentElement)
);
if (documentReplaced) {
const timeoutId = setTimeout(_handleListenerDetached);
window.dispatchEvent(new CustomEvent(eventName, {detail: timeoutId}));
}
}).observe(document, { childList: true });
function _handleListenerDetached() {
// reattach event listener
window.addEventListener(eventName, _handleListenerStillAttached);
// run registered callbacks
callbacks.forEach((callback) => callback());
}
function _handleListenerStillAttached(event) {
clearTimeout(event.detail);
}
return {
addListener: c => void callbacks.add(c),
hasListener: c => callbacks.has(c),
removeListener: c => callbacks.delete(c)
}
})();
Background
I would like to simulate the "active" behavior that most browsers support for links when they are clicked. For any element and event I would like to be able to add a css class to the element for the duration of the event. So in psuedo jQuery code I'd like to do this:
$(".button").click(function() { alert('clicked'); });
.....
$(".button").each( function()
{
var self = $(this);
var innerClick = self.click;
self.click( function()
{
self.addClass('active');
innerClick();
self.removeClass('active');
});
});
Bonus points for anyone that can suggest a solution that is robust enough to maintain the wrapping if I bind further handlers to 'click'.
The problem is there is no general way to determine when an asynchronous call is done. You would need to set some kind of flag after the AJAX call (for example) is done and poll it with setInterval.
With synchronous functions, the browser probably won't update the display until the click handler is done anyways, so you would never see the active state. You can get around that by using setTimeout to remove the active class after 1/10 of a second. For this case, you could write a function like this:
function makeClickHandler(innerFunction) {
return function(event) {
var self = this;
$(self).addClass('active');
innerFunction.call(self, event);
window.setTimeout(function() {
$(self).removeClass('active');
}, 100);
}
}
You could then wrap any click handlers in the makeClickHandler function:
$(".button").click(makeClickHandler(function() { alert('clicked'); }));
Here's an example of how you could handle asynchronous events, but at this point, it might just be easier to add/remove the class in the individual event handlers.
function makeAsyncClickHandler(innerFunction) {
return function(event) {
var self = this;
$(self).addClass('active').data("done", false);
innerFunction.call(self, event);
var intervalID = window.setInterval(function() {
if ($(self).data("done")) {
$(self).removeClass('active');
window.clearInterval(intervalID);
}
}, 100);
}
}
The inner function would have to call
$(this).data("done", true);
when the asynchronous part is finished.
It would probably be easier to write "start" and "finish" functions that you can call on the element at the beginning of the function and after the event is finished.
Well, for the bonus point, it comes for free as you can as many handler you want to the same event.
No regarding your initial question, I didn't get the point. Do you want to simulate the "active" behavior on non-link element? If so, binding the mousedown/mouseup envents to addClass/removeClass methods should do the trick.