attaching to an event - but only conditional - javascript

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();
}

Related

how to prevent a event listener function to be triggered several times at the same time

my question is easy I'm using discord js and have this event listener in my code.
this listener function is triggered when a user sends a particular message in a channel. But if 2 users send this particular message at the same time the listener function is triggered twice at the same time. How can I prevent this ?
If you are happy to ignore the second message coming through you could have a look at wrapping your function with debouncing, which would cause it to only trigger once if called in short succession.
Lodash has a package for this that can be imported individually
import { myFunc } from 'somewhere';
import { debounce } from 'somewhereElse';
const DEBOUNCE_DELAY_MS = 500;
const myDebouncedFunc = debounce(myFunc, DEBOUNCE_DELAY_MS);
// Call myDebouncedFunc twice immediately after each other, the
// debouncing will result in the function only getting called max once
// every 500ms;
myDebouncedFunc();
myDebouncedFunc();
Otherwise, if you need to have both messages processed, just not at the same time, then you would need something like a queue for processing these events. Then you could process these messages in an interval for example.
// Some lexically available scope
const myQueue = [];
// Event handler
const myHandler = (msg) => {
myQueue.push(msg);
}
// Interval processing
setInterval(() => {
if (myQueue.length > 0) {
const msgToProcess = myQueue.shift();
processMessage(msgToProcess);
}
}, 500)
There are multiple solutions to this problem depending on your needs.
Unregister the event listener as soon as the even comes. In most libraries, you can easily achieve by registering your event using once() method, instead of "on()"
Use debounce() to aggregate all the events triggered within a certain amount of time into one.
The same or similar methods exist in most of JS libraries similar to lodash.

onChange / onInput alternatives?

I'm creating a typing test page and running into a small issue.
I'm trying to start the timer when the user types, and using onInput works, but the problem is that it registers every time I type into the textArea which causes the starttimer function to repeat itself.
onChange works but it only works after I click outside the page which makes sense, but is not what im looking for. I need the timer to start as soon as the user starts typing. Also with onChange the stop function works, but the timer starts after i click outside of the page.
Is there a JS event that fits what i'm looking for, or is there a way to change onInput or onChange to fix the problem i'm having.
JavaScript
document.getElementById("test-area").onchange = function () {
startTimer(1);
};
Thank you.
You need to listen to the input event because as you said change is only triggered after the input loses focus which is not what you want. You also need to handle the event only once.
document.getElementById("test-area").addEventListener("input", () => {
startTimer(1);
}, {once: true});
Note that this removes the event handler after it is fired. If you need to run it again you will have to register the event one more time. Maybe in your timer callback.
Try like this, In JavaScript, using the addEventListener() method:
The addEventListener() method attaches an event handler to the specified element.
document.getElementById("test-area").addEventListener("change", function () {
if(!this.value.length){
startTimer(1);
}
}, {once : true});
Have you tried onfocus ? its not exactly when they start typing but it works. Another option would be that you use onInput and on the clocks start function change a boolian -isRunning - to true. then put a if (isRunning) return. something like:
function start() {
if(isRunning) return;
isRunning = true;
}
and then change the boolian to false when you stop onChange
Some solutions:
Variant 1
Just create a flag:
var timerStarted = false; // Was the timer started?
document.getElementById("test-area").oninput = function () {
if(timerStarted) return; // If timer was started - do nothing
startTimer(1); // Else - start the timer
timerStarted = true; // Remember what we've started the timer
};
Variant 2
(A bit shorter)
document.getElementById("test-area").addEventListener("input", function () {
startTimer(1);
}, {once: true}); // Good thing about addEventListener
// With this it will run the event only single time
More here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
Can you use JQuery? If so it has a method .one() which will execute the function only once. You can freely use keydown/keyup event handler then. For eg.
<script>
$(document).ready(function(){
$("#test-area").one("keydown", function() {
alert("hey");
});
});
</script>

addeventlistener built multiple times

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;
})

Unable to unbind event listener - trouble with turbolinks caching

I'm binding then unbinding the ready event listener to the document.
$(document).bind("ready", readyEventHandler);
function readyEventHandler() {
// run some code
$(document).unbind("ready");
}
The code produces no errors and will work. However, my javascript is cached and duplicates the code so I'll end up with having this code run more than once if I go back and then forward a page in the browser. When this happens, the ready event listener is not called at all. Am I properly unbinding this event listener? I know the caching issue becomes problematic(it's own separate issue) but I just want to bind the ready event listener, have it run code, then unbind it.
Not so sure it will help, but here are my 2 cents - instead of trying to unbind the readyEventHandler - make sure that if you run the function once it will not run twice:
var readyHandlerRun = false;
$(document).bind("ready", readyEventHandler);
function readyEventHandler() {
if (readyHandlerRun) {
return;
}
readyHandlerRun = true;
// Rest of your code...
}
Another options that popped just now:
$(document).bind("ready", readyEventHandler);
function readyEventHandler() {
readyEventHandler = function() { }
console.log('ready');
// Rest of your code...
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
UPDATE (by #jason328)
After talking with Dekel he provided me the appropriate answer.
$(document).bind("ready", function() {
readyEventHandler();
readyEventHandler = function() { }
});
Elegant and works like a charm!
if you just would like to use an eventhamdler only once, you could use one instead of bind
$(document).one("ready", function() {
// code to run on document ready just for once
});

Determine from function when an event stops with JavaScript

Simplified scenario:
I have a click event on a button
Inside the event I call a function X
Function X returns a value
Click event changes DOM using received data (actually renders a jQuery Template)
SAMPLE CODE:
$("button").click(function()
{
var response = FunctionX();
doTemplateRendering(response); //(*)
});
function FunctionX()
{
//some code
return some_value;
//The click event has finished, so now make a little adjust to the generated dom (*)
}
The questions are:
Do I have a way to determine when the click stops form FunctionX()?
Or is there a way that doTemplateRendering() triggers something that I can capture from FunctionX() so it doesn't matter if I return a value, because at some point I'm going to be able to execute the extra code that I need
The reason behind this is that this is part of a framework and I can't change the click event.
You can't return a value from a function and then do further processing within the same function. But you can schedule some code to run later using setTimeout():
function FunctionX()
{
//some code
setTimeout(function() {
//The click event has finished, so now make a little adjust to the generated dom (*)
}, 5);
return some_value;
}
JS is single-threaded (if we ignore web-workers), so the function you pass to timeout won't be executed until after the click event finishes.
Alternatively, you can bind a second click handler to the same button and do your extra processing there - jQuery guarantees that event handlers (for the same event and element) will be run in the order that they're bound.
Not sure if this works for you, but couldn't you use a callback function that you pass to FunctionX?
$("button").click(function()
{
var fn = funcion(){response){
doTemplateRendering(response);
}
FunctionX(fn);
});
function FunctionX(fn)
{
//some code
fn(some_value);
//The click event has finished, so now make a little adjust to the generated dom (*)
}

Categories