Disable Button with a bind function? - javascript

Hello how can i disable a button with the bind function for 10 sec?
jQuery('#wsf-1-field-155').bind('click', function() {
ScanRegistration();
setTimeout(function() {
jQuery('#wsf-1-field-155').bind();
}, 10000);
})

I solved this Problem with this, I change .removeProp with .removeAttr
jQuery('#wsf-1-field-155').on('click', function() {
jQuery(this).prop('disabled', true);
ScanRegistration();
setTimeout(() =>
jQuery(this).removeAttr('disabled'), 20000);
})

Here's a plain JavaScript solution. scanRegistration() just counts up to 10sec.
Details are commented in example
// count and interval variables should be declared outside of function
let i = 0;
let int;
// Reference the <button>
const btn = document.querySelector('#GO');
// Function enables the <button>
const enableBtn = () => btn.disabled = false;
/*
Bind the "click" event to the <button>
Disable <button>
call scanRegistration()
call enableBtn() #10 seconds
*/
btn.onclick = function(event) {
this.disabled = true;
scanRegistration();
setTimeout(() => {
enableBtn();
}, 10000);
};
// Function logs every second
const logScan = i => console.log("SCAN: " + i);
/*
Set an interval at the rate of 1 second
Increment the count variable i
call logScan()
If i is equal to or more than 10 end interval
*/
function scanRegistration() {
console.log("START SCAN");
int = setInterval(() => {
i++;
logScan(i);
if (i >= 10) {
console.log("END SCAN");
clearInterval(int);
}
}, 1000);
}
<button id="GO">GO!</button>

.bind() is deprecated. You should use .on() instead.
You don't use event binding to disable a button, you set its disabled attribute. Then use removeAttr() to re-enable it after 10 seconds.
jQuery('#wsf-1-field-155').on('click', function() {
$(this).attr('disabled', true);
ScanRegistration();
setTimeout(() =>
$(this).removeAttr('disabled'), 10000);
})

Related

Avoid numbers incrementing multiple times when calling a function multiple times

I have written a quantity selector function to display on a page. The page can open some modals, which need to have another quantity selector within each.
I am calling the function within the main page, and also within the modal (to enable the functionality once the modal is displayed.)
When I adjust the quantity in the modal, close the modal, and adjust the quantity on the main page, the quantity increments/decrements double (or 3 times if I was to call the function 3 times.)
Is there a way to "reset" each of these event listeners/functions, to only adjust for their respective elements?
I've looked into "removeEventListener" but haven't had any joy in implementing this within my code.
Example of my work so far here (you can see what I mean if you click the buttons.)
https://codepen.io/777333/pen/zYoKYRN
const quantitySelector = () => {
const qtyGroups = document.querySelectorAll('.qty-group');
if(qtyGroups) {
qtyGroups.forEach((qtyGroup) => {
const qtyDecrease = qtyGroup.querySelector('[data-quantity-decrease]');
const qtyIncrease = qtyGroup.querySelector('[data-quantity-increase]');
const qtyInput = qtyGroup.querySelector('[data-quantity-input]');
const disableEnableDecrease = () => {
if(qtyInput.value == 1) {
qtyDecrease.disabled = true;
} else {
qtyDecrease.disabled = false;
}
};
qtyDecrease.addEventListener('click', (event) => {
event.preventDefault();
if(qtyInput.value > 1) {
qtyInput.value--;
}
disableEnableDecrease();
});
qtyIncrease.addEventListener('click', (event) => {
event.preventDefault();
qtyInput.value++;
disableEnableDecrease();
});
qtyInput.addEventListener('keyup', () => {
disableEnableDecrease();
});
});
}
};
quantitySelector(); // called within main page
quantitySelector(); // called within modal
The issue at hand is that each time you're calling the function, a new event handler is added on top of the previous ones. The best way to avoid this is through Event Delegation where you add a global event handler only once.
// A global event handler
document.addEventListener(
"click",
function (event) {
// Find the qty-group if clicked on it
const qtyGroup = event.target.closest(".qty-group");
// Stop if the click was elsewhere
if (qtyGroup) {
// Get your elements
const qtyDecrease = qtyGroup.querySelector("[data-quantity-decrease]");
const qtyIncrease = qtyGroup.querySelector("[data-quantity-increase]");
const qtyInput = qtyGroup.querySelector("[data-quantity-input]");
const disableEnableDecrease = () => {
if (qtyInput.value == 1) {
qtyDecrease.disabled = true;
} else {
qtyDecrease.disabled = false;
}
};
// Match your elements against what was clicked on.
if (event.target == qtyDecrease) {
event.preventDefault();
if (qtyInput.value > 1) {
qtyInput.value--;
}
disableEnableDecrease();
}
if (event.target == qtyIncrease) {
event.preventDefault();
qtyInput.value++;
disableEnableDecrease();
}
}
},
false
);
Instead of listening to individual elements, you can capture all the clicks on the document, and then finding those that click on elements of interest. You can make a second event handler for the keyup event.
You can save the value of qtyInput on mousedown event and then in the increment you add or subtract one from the saved value instead of the current value of the input.
const quantitySelector = () => {
const qtyGroups = document.querySelectorAll('.qty-group');
if(qtyGroups) {
qtyGroups.forEach((qtyGroup) => {
const qtyDecrease = qtyGroup.querySelector('[data-quantity-decrease]');
const qtyIncrease = qtyGroup.querySelector('[data-quantity-increase]');
const qtyInput = qtyGroup.querySelector('[data-quantity-input]');
const disableEnableDecrease = () => {
if(qtyInput.value == 1) {
qtyDecrease.disabled = true;
} else {
qtyDecrease.disabled = false;
}
};
let savedValue = null;
const saveState = (evebt) => savedValue = Number(qtyInput.value);
qtyDecrease.addEventListener('mousedown', saveState)
qtyIncrease.addEventListener('mousedown', saveState)
qtyDecrease.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
if(qtyInput.value > 1) {
qtyInput.value = savedValue - 1;
}
disableEnableDecrease();
});
qtyIncrease.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
qtyInput.value = savedValue + 1;
disableEnableDecrease();
});
qtyInput.addEventListener('keyup', () => {
disableEnableDecrease();
event.stopPropagation();
});
});
}
};
quantitySelector();
quantitySelector();
There is a method called removeEventListener (MDN) but I suggest you to reshape your code such that you do not add event listener if they are already present.
Put all of your addEventListener just when you create your elements, or in a "document ready" callback if they are instantiated by HTML code. Then, when you open your modal, just update your values.
UPDATING YOUR CODE
// hide/show modal function
function toggleModal() {
let modal = document.getElementById('modal');
modal.style.display = modal.style.display == 'none' ? 'block' : 'none';
}
// your document ready function
function onReady() {
const qtyGroups = document.querySelectorAll('.qty-group');
if(qtyGroups) {
qtyGroups.forEach((qtyGroup) => {
const qtyDecrease = qtyGroup.querySelector('[data-quantity-decrease]');
const qtyIncrease = qtyGroup.querySelector('[data-quantity-increase]');
const qtyInput = qtyGroup.querySelector('[data-quantity-input]');
const disableEnableDecrease = () => {
if(qtyInput.value == 1) {
qtyDecrease.disabled = true;
} else {
qtyDecrease.disabled = false;
}
};
qtyDecrease.addEventListener('click', (event) => {
event.preventDefault();
if(qtyInput.value > 1) {
qtyInput.value--;
}
disableEnableDecrease();
});
qtyIncrease.addEventListener('click', (event) => {
event.preventDefault();
qtyInput.value++;
disableEnableDecrease();
});
qtyInput.addEventListener('keyup', () => {
disableEnableDecrease();
});
});
}
// attach hide/show modal handler
const toggle = document.getElementById('modal_toggle');
toggle.addEventListener('click', toggleModal);
}
onReady();
<div class="qty-group">
<button data-quantity-decrease disabled>-</button>
<input data-quantity-input value="1">
<button data-quantity-increase>+</button>
</div>
<div class="qty-group" id="modal" style="display: none;">
<button data-quantity-decrease disabled>-</button>
<input data-quantity-input value="1">
<button data-quantity-increase>+</button>
</div>
<button id="modal_toggle">Toggle Modal</button>
REFACTORING
It is better in such cases to reason as Components. Components ensure code encapsulation, maintainability, reusage, single responsability and many other usefull principles:
// hide/show modal function
function toggleModal() {
// get the modal
let modal = document.getElementById('modal');
// hide the modal
modal.style.display = modal.style.display == 'none' ? 'block' : 'none';
// reset the input of the modal
modalInputReference.reset();
}
function createQuantityInput(target, initialQuantity=1, min=1, max=10, step=1) {
let quantity = 0;
// assign and check if should be disable, also bind to input value
let assign = (q) => {
quantity = Math.max(Math.min(q, max), min);
decrease.disabled = quantity <= min;
increase.disabled = quantity >= max;
input.value = quantity;
};
// CREATION
// This part is not mandatory, you can also get the elements from
// the target (document.querySelector('button.decrease') or similar)
// and then attach the listener.
// Creation is better: ensure encapsulation and single responsability
// create decrease button
let decrease = document.createElement('button');
decrease.addEventListener('click', () => { assign(quantity - step); });
decrease.innerText = '-';
// create increase button
let increase = document.createElement('button');
increase.addEventListener('click', () => { assign(quantity + step); });
increase.innerText = '+'
// create input field
let input = document.createElement('input');
input.value = quantity
input.addEventListener('change', () => { assign(parseFloat(input.value)); });
// resetting the quantity
assign(initialQuantity);
// appending the new component to its parent
target.appendChild(decrease);
target.appendChild(input);
target.appendChild(increase);
// return a reference to manipulate this component
return {
get quantity() { return quantity; },
set quantity(q) { assign(q); },
assign,
reset: () => assign(initialQuantity)
};
}
// this will be your modal reference
let modalInputReference;
function onReady() {
// inject all qty-group with a "quantityInput" component
document.querySelectorAll('.qty-group').forEach(elem => {
let input = createQuantityInput(elem);
if (elem.id == 'modal') {
// if it is the modal I save it for later use
// this is just an hack for now,
// a full code should split this part into a "modal" component maybe
modalInputReference = input;
}
});
// emualte the modal
let toggle = document.getElementById('modal_toggle')
toggle.addEventListener('click', toggleModal)
}
// this function should be wrapped by a
// $(document).ready(onReady) or any other
// function that ensure that all the DOM is successfully loaded
// and the code is not executed before the browser has generated
// all the elements present in the HTML
onReady();
<div class="qty-group"></div>
<div class="qty-group" id="modal" style="display: none;"></div>
<button id="modal_toggle">Toggle Modal</button>
It is shorter (without comments) and also more maintenable. Don't trust who says it is overengineered, it is just kind of time to learn to reason this way, then is much easier and faster. It is just a time investment to waste less time in the future. Try figure out why React or Angular(JS) have climbed the charts of the best frameworks so fast.

Function inside event listener triggers only on it's initialization

var init = true;
$('#btn').on('click', delay(function() {
$('#text').append('click');
init = false;
}, 100));
function delay(fn, ms, enabled = true) {
$('#text').append(init);
// if(init) disable delay
let timer = 0;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(fn.bind(this, ...args), ms || 0);
}
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<button id='btn'> TRIGGER </button>
<div id="text"></div>
Init is a global variable which is meant to be used inside delay function to disable delay (init true/false) only on event listener initialisation.
The problem is that the delay function is triggered only once and ignores the change (to false) of the init variable.
For example, try clicking the trigger button. The init variable value is printed only for the first time.
You are calling the delay function in a wrong way in the click handler. You have to call it like so:
$('#btn').on('click', function () {
delay(function() {
$('#text').append('click');
init = false;
}, 100);
});
You will have to check for the value of init inside the function, like this:
$('#btn').on('click', delay(function() {
if(init) {
$('#text').append('click');
init = false;
}
}, 100));
At the moment I don't know why append is not working but with a little workaround you can obtain what you want. Concatenate the original text and the actual one and use text() to set it again:
var init = true;
$('#btn').on('click', function() {
$('#text').text(init);
setTimeout(myDelay, 5000);
});
function myDelay() {
let originalText = $('#text').text();
init = false;
console.log("init is false");
console.log("original text displayed: " + originalText);
$('#text').text(originalText + " " + init);
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<button id='btn'> TRIGGER </button>
<div id="text"></div>

Set a delay between each click in loop

I would like to simply set a delay between each click.
Shown below is the script I created. It works fine with clicking each element the way I want it to. The problem is that it clicks each element almost at the same time causing me horrible lag.
Original Code.
var timerVar = setInterval (function() {DoMeEverySecond (); }, 5000); //
<< set to 2 seconds.
function DoMeEverySecond ()
{
(function() {
document.getElementsByName("zTab")[0].click();
document.getElementsByName("zButton")[0].click();
document.getElementsByName("zClose")[0].click();
})();
}
Would it be possible to do something like this.
var timerVar = setInterval (function() {DoMeEverySecond (); }, 5000); //
<< set to 2 seconds.
function DoMeEverySecond ()
{
(function() {
document.getElementsByName("zTab")[0].click();
-----------A DELAY HERE!-----------
document.getElementsByName("zButton")[0].click();
---------- ANOTHER ONE HERE! ----------------
document.getElementsByName("zClose")[0].click();
})();
}
The code is very simple, I tried to explain it as best as I could. Could someone help me out on this piece of code
Here's a live demo of a potential solution. See the inline comments:
// Lets just add some logging here for when the buttons are clicked
document.querySelectorAll('.z-tab').forEach(element => {
element.addEventListener('click', e => console.log('Z Tab'));
});
document.querySelectorAll('.z-button').forEach(element => {
element.addEventListener('click', e => console.log('Z Button'));
});
document.querySelectorAll('.z-close').forEach(element => {
element.addEventListener('click', e => console.log('Z Close'));
});
// Let's take advantage of async/await
async function DoMeEverySecond () {
const elementClassNames = ['.z-tab', '.z-button', '.z-close'];
for (let i = 0; i < elementClassNames.length; i++) {
const element = document.querySelectorAll(elementClassNames[i])[0];
await delayFor(1000); // Let's wait 1000ms between each click
element.click()
}
}
// Delay function that will resolve a promise after the setTimeout delay has been passed.
function delayFor(delay) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, delay);
});
}
DoMeEverySecond ();
<h2>Z Tabs</h2>
<button class="z-tab">1</button> <button class="z-tab">2</button>
<h2>Z Buttons</h2>
<button class="z-button">1</button> <button class="z-button">2</button>
<h2>Z Close</h2>
<button class="z-close">1</button> <button class="z-close">2</button>
Shure you can add delays, but as #Brock Adams mentioned, better use a different approach, like a promise-chain, to be shure all clicks were triggered, before looping again.
I assume a delay of 1 second, you can change this, but be aware, that if you add a delay in total of more that 5 seconds, the click-actins will overlap per interval.
So your code with delays:
function doMeInInterval () {
document.querySelector("[name=zTab]").click();
setTimeout(function() {
document.querySelector("[name=zButton]").click();
setTimeout(function() {
document.querySelector("[name=zClose]").click();
}, 1000);
}, 1000);
}
var timerVar = setInterval(doMeInInterval, 5000);

Javascript double click event issue

I have a click listener on a DOM element (no jQuery):
element.addEventListener('click', (e) => {
// some code
});
and obviously when I click, the code runs and everything is fine.
The problems is that when I double click, the code runs twice and I don't want this behavior (when I double click I want it to act like a single click and run the code once).
One possibility is to use Date to check to see if the last click that triggered the function proper was less than 1 second ago:
const element = document.querySelector('div');
let lastClick = 0;
element.addEventListener('click', (e) => {
const thisClick = Date.now();
if (thisClick - lastClick < 1000) {
console.log('quick click detected, returning early');
return;
}
lastClick = thisClick;
console.log('click');
});
<div>click me</div>
If you want the function proper to run only once the last click was more than 1 second ago (rather than the last function proper run being more than one second ago), change it so that lastClick is assigned to inside the if (thisClick - lastClick < 1000) { conditional:
const element = document.querySelector('div');
let lastClick = 0;
element.addEventListener('click', (e) => {
const thisClick = Date.now();
if (thisClick - lastClick < 1000) {
console.log('quick click detected, returning early');
lastClick = thisClick;
return;
}
lastClick = thisClick;
console.log('click');
});
<div>click me</div>
debounce the event to trigger in a certain period of time:
const element = document.querySelector('button');
let time;
element.addEventListener('click', (e) => {
if (time) {
clearTimeout(time);
}
time = setTimeout(() => console.log('runs after last click'), 500);
});
<button>Click!!!</button>
The most straightforward solution for this is to create a variable that acts as a gate that is reset after a certain time (one second in this example).
var el = document.querySelector('p');
var clickAllowed = true;
el.addEventListener('click', e => {
if (!clickAllowed) {
return;
}
clickAllowed = false;
setTimeout(() => clickAllowed = true, 1000);
// do stuff here
console.log('test');
});
<p>Test</p>
On the first click, your code will run and the "gate" will close to stop a second click. After one second, the "gate" opens to allow the code to run again.
you can set value to one of the input and see if the value is changed
function trigger(){
if(document.getElementById('isClicked').value ==0 ){
console.log('clicked for the first time');
document.getElementById('isClicked').value = 111;
setTimeout(function(){
document.getElementById('isClicked').value = 0;
}, 1000);
}
}
<button onclick='trigger()'>click me </button>
<input type='hidden' value=0 id='isClicked' />
this code working for you
var el=document.getElementById('demo');
window.clicks=0;
// handle two click
el.addEventListener('click',function(){
clicks++;
el.innerHTML='clicks: '+clicks;
setTimeout(function(){
if (clicks == 1) {
runYourCode();
clicks=0;
}
else{
clicks=0;
return;
}
},400);
})
// dblclick event
el.addEventListener('dblclick',function(){
runYourCode();
})
function runYourCode(){
document.getElementById('text').innerHTML += '<br>Run your Code...';
};
#demo{
background:red;
padding:5px 10px;
color:white;
max-width:100px;
}
<p id="demo">click me!</p>
<p id="text">
log:<br>
</p>

On keypress, when stop after X seconds call function

I have a text input and a textarea and I'm passing the value from the input to the textarea. I am trying to do, when you type something in the input and you stop, after 2 seconds show the values to the textarea.
In this example the textarea gets the input's value instantly:
http://jsfiddle.net/DXMG6/
So i want, when you type and stop, after 2 seconds give the value.
How can I achieve this? I tried to use setTimeout but when the 2 seconds pass, then it keeps getting the value instantly. So basically it works for the first 2 seconds.
You have to reset the timer everytime the user presses the key again:
jQuery(function($){
function changeFn(){
alert('Changed');
}
var timer;
$("#string").bind("keyup", function(){
clearTimeout(timer);
timer = setTimeout(changeFn, 2000)
});
});
Once i made this plugin called bindDelay for jQuery:
$.fn.bindDelay = function( eventType, eventData, handler, timer ) {
if ( $.isFunction(eventData) ) {
timer = handler;
handler = eventData;
}
timer = (typeof timer === "number") ? timer : 300;
var timeouts;
$(this).bind(eventType, function(event) {
var that = this;
clearTimeout(timeouts);
timeouts = setTimeout(function() {
handler.call(that, event);
}, timer);
});
};
Used like a normal bind method but the last argument is the delay before firing the handler (in mil sec):
$("input").bindDelay('keyup', function() {
$("textarea").text( $(this).val() );
}, 2000);
See fiddle: http://jsfiddle.net/c82Ye/2/
And you unbind and trigger it like normal:
$("input").unbind("keyup");
$("input").trigger("keyup");
setTimeout returns an ID of the "job". what you have to do is to clearTimeout(id) every type and setTimeout again:
var tID = null;
onclick() {
if (tID !== null) clearTimeout(tID);
tID = setTimeout(function() { /*Do domething*/ }, 2000);
}
What you need to do is set a timeout, and save the resulting timeout id. Then you need to check if the timeout id has been saved at each keypress. If the timeout is set, clear the timeout and reset it. Something like this:
var timeoutId = null;
var myFunc = function() {
timeoutId = null;
// Do stuff
};
var myEventHandler = function() {
if (timeoutId) {
window.clearTimeout(timeoutId);
}
timeoutId = window.setTimeout(myFunc, 2000);
};
...or check the updated fiddle:
http://jsfiddle.net/DXMG6/5/
I've updated your fiddle
This will update the textarea value 2 seconds after you end editing the text.
The relevant part is this: we keep a reference to a timeout, when the keyup event is fired we clear the previous timeout and we start a new timeout, that will fire in 2 seconds.
var timeout = null;
$("#string").on("keyup keypress paste mouseup", function () {
clearTimeout(timeout);
timeout = setTimeout(function() {
// ... your code here
}, 2000);
});
Try something like this. Use setTimeout, but each time a key is pressed, reset the timer and start over...
http://jsfiddle.net/DXMG6/10/
var textTimer=null;
$("#string").on("keyup keypress paste mouseup", function () {
if (textTimer) clearTimeout(textTimer);
textTimer = setTimeout(function(){
var a = $('#string').val();
$('#rdonly').html(a);
}, 2000);
});
$('.btn').click(function() {
$('#rdonly').text('');
$('#string').val('');
});
You just need to modify your code as follows:
var timeoutId = 0;
$("#string").on("keyup keypress paste mouseup", function () {
var a = $('#string').val();
// Cancel existing timeout, if applicable
if (timeoutId > 0) {
window.clearTimeout(timeoutId);
}
// Start a timeout for 2 seconds- this will be cancelled above
// if user continues typing
timeoutId = window.setTimeout(function () {
$('#rdonly').html(a);
}, 2000);
});

Categories

Resources