Why does JQuery $.post submit twice? - javascript

Why does this call my Action twice? For background, using .change only works when a user clicks somewhere else on the form. Also .change will not fire when the browser is closed. Hence the timer combined with the data-dirty property check. Thanks in advance.
var timeoutId;
$(function() {
var formElements = $('#myformid').find('input, select, textarea').add('[form=myformid]').not(':disabled').each(function() {
$(this).attr('data-dirty', false).on('input propertychange change', function() {
var changedText = $(this).val();
var commentID = $(this).attr('id');
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
// Runs 1 second (1000 ms) after the last change
// Saving to DB Here
$.post('#Url.Action("UpdateComment", "CommentJournal")', {
"ChangedText": changedText,
"commentID": commentID
});
$(this).attr('data-dirty', true);
}, 1000);
});
});
});
//Controller
[HttpPost]
[AllowAnonymous]
public ActionResult UpdateComment(string changedText, string commentID) {
return null;
}

Probably because both input and change events are being fired, with the change firing more than 1000ms after input fired, since change only fires when focus leaves the control. Example:
var timerId = 0;
$("#field1").on("input change", function(event) {
var type = event.type;
clearTimeout(timerId);
timerId = setTimeout(function() {
console.log("timeout fired for '" + type + "'");
}, 1000);
});
<p>Type something in the first field, wait at least a second, then tab out of the field:</p>
<div>
<label>
First field:
<input type="text" id="field1">
</label>
</div>
<div>
<label>
second field:
<input type="text" id="field2">
</label>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
If you hook input, there's no need to hook change.

Related

How to prevent spamming an ajax call when using arrows inside input type number

I've got a function that triggers an ajax call on change of an input with type number. When I click the arrows inside the input field and keep my finger on the click button, the number increases/decreases fast but only sends the ajax call after I release my finger. This is great.
The problem is when I focus inside the input and use my arrow keys on my keyboard to increase or decrease the input, my code sends an ajax call for every time the number changes, which can be alot. This means my ajax calls break because too many are sent in a short amount of time.
How can I get the same behaviour like when clicking the arrows inside the input type for when I press the arrow buttons on my keyboard?
My code:
HTML
<div class="prodinfoquantity">
<input class="productid" type="hidden" name="productid" value="60b0f82b46193" />
<input type="number" class="form-control aantal" value="10" min="1" />
</div>
My jquery
$('.checkoutwrap .aantal').on("change", function(e) {
e.preventDefault();
var productid = $(this).siblings('.productid').val();
var quantity = $(this).val();
var form_data = $("#formsid form").serialize();
$.ajax({
type:'post',
url:"checkout/prices.php",
data:({productid: productid, quantity: quantity}),
success:function(data){
$($pricediv).empty().append( data );
refreshcoupon(true);
}
});
});
This is what it looks like if I use my keyboard:
I tried using keyup instead of change but then it stops sending an ajax call when I click the arrow buttons that are inside the input field.
Using the the following function from one of the comments above:
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
You can then apply it to your on change function like this:
$('.checkoutwrap .aantal').on("change", debounce(function(e) {
e.preventDefault();
var productid = $(this).siblings('.productid').val();
var quantity = $(this).val();
var form_data = $("#formsid form").serialize();
$.ajax({
type:'post',
url:"checkout/prices.php",
data:({productid: productid, quantity: quantity}),
success:function(data){
$($pricediv).empty().append( data );
refreshcoupon(true);
}
});
}, 300)) ;
What you want is known as throttling or debouncing.
Here is a simplified version
const DELAY = 300;// adjust to suit needs
$('.aantal').on("change", function(e) {
let $input = $(this),timer = $input.data('timer');
// clear previous timer
clearTimeout(timer);
// set new one
timer = setTimeout(doStuff, DELAY);
$input.data('timer', timer)
function doStuff() {
console.log('Do stuff');
$input.data('timer', null)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="prodinfoquantity">
<input class="productid" type="hidden" name="productid" value="60b0f82b46193" />
<input type="number" class="form-control aantal" value="10" min="1" />
</div>

Javascript. Submit form after user stop typing

How i can submit form after user complete typing input?
Example: The user started typing. After the introduction of several characters stopped. The form was submited.
Here is a example for you.
var timer;
var doneTypingInterval = 3000; // wait 3 seconds
var $input = $('input');
$input.on('keyup', function () {
clearTimeout(timer);
timer = setTimeout(doneTyping, doneTypingInterval);
});
$input.on('keydown', function () {
clearTimeout(timer);
});
function doneTyping () {
alert('done')
}
Html
<input type="text">
But i don't recommend this. It can be annoying for your user's.
i hope this sample helps :
HTML
<form>
<input name="query" id="query">
<p id="message" > </p>
</form>
JS
function submitForm(){
//do some stuff
}
jQuery(document).ready(function(){
window.timeout=null;
jQuery('#query').on('keyup', function(event){
clearTimeout(window.timeout);
window.timeout = setTimeout(submitForm, 1000); // waits 1 second for user to press a new key
});
});

Stop jQuery keyup from triggering with delay

I'm trying to create an email validation field that shows an error message if the email doesn't fit the standards. For this I used keyup as event so the message would be visible as soon as the user entered a wrong email.
But this way every time the user enters a character the message shows, so I thought I would add a delay and found a function for this but this does indeed add a delay but still shows every time the message gets triggered.
Does someone know how to make sure the event only gets called once after the user has stopped for a certain time period?
JSFiddle
<table>
<tr>
<td>Email</td>
<td>
<input type="text" class="email" value="supplier#supplier.com">
</td>
<td><span class="message"></span></td>
</tr>
</table>
function isEmail(email) {
var regex = /^([a-zA-Z0-9_.+-])+\#(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
return regex.test(email);
}
$('.email').on('keyup', function() {
var delay = (function() {
var timer = 0;
return function(callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
var email = $('.email').val();
$(this).closest('tr').find('.message').hide();
if (isEmail(email) == false) {
delay(function() {
$('.email').closest('tr').find('.message').hide().html('Invalid email.').fadeIn('fast');
}, 1000);
} else {
$(this).closest('tr').find('.message').hide();
}
});
The logic of your timeout is a little flawed in the above. Note that a better method which allows users to enter their email without validation the first time is to check the validity of the email on blur of the input. If you wanted to you could then check validity of the email after every keypress following the first blur event. Something like this:
function checkEmailValidity($el) {
var regex = /^([a-zA-Z0-9_.+-])+\#(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
var $msg = $el.closest('tr').find('.message').hide();
if (!regex.test($el.val()))
$msg.html('Invalid email.').fadeIn('fast');
}
var timer;
$('.email').on({
blur: function() {
$(this).data('hasBlurred', true);
checkEmailValidity($(this));
},
keyup: function() {
clearTimeout(timer);
timer = setTimeout(function() {
$(this).data('hasBlurred') && checkEmailValidity($(this));
}.bind(this), 250);
}
});
Working example

JS onChange isn't fired if interval is at under a minute

I'm using the onChange attribute to flip a global variable to true. The page will then refresh if the global variable unfinished is false. If it's true, it should just show an alert. This works 90% of the time. On a few cases where the interval is at less than a minute, the unfinished variable will be ignored and the page will refresh anyway.
Is there a better and less hack-job way of achieving a way to block page refreshes? Basically it looks to me like onChange isn't firing if my interval is at less than 1 minute.
HTML
<textarea name="comment" id="thecommentbox" style="width: 100%;" onChange="blockRefresh();" class="thecommentbox form-control" style="height: 70px;" placeholder="Comment."></textarea>
JS
var unfinished = false;
function blockRefresh() {
unfinished = true;
document.getElementById('refreshBlocked').style.display = 'inline';
console.log('Refresh blocked.');
}
setTimeout(function(){
if(unfinished) {
alert("unfinished comment");
}
else {
window.location = window.location.pathname;
}
}, 600000); //ten minutes is 600,000
var timeRemaining = 600000;
var timer = setInterval('countdown()',60000);
function countdown() {
timeRemaining -= 60000;
document.getElementById('refreshTimer').innerHTML = "<strong>" + timeRemaining/60000 + "</strong>";
}
After some reading of this Textarea onchange detection and testing of the answers I think the best answer is to use a combination of the input and onpropertychange events.
In addition the user could press a key that doesn't actually change anything in the field you will need to get the value of the text field when the page first loads. When a change occurs compare the current value against that value.
(See code below)
var unfinished = false;
var textString = document.getElementById("thecommentbox").value;
function blockRefresh() {
if (textString != this.value) {
window.unfinished = true;
console.log('Refresh blocked.');
}
}
var txtBox = document.getElementById('thecommentbox');
if (txtBox.addEventListener) {
txtBox.addEventListener('input', blockRefresh, false);
} else if (txtBox.attachEvent) {
txtBox.attachEvent('onpropertychange', blockRefresh);
}
This will detect pasting from the context menu and work in IE in addition to most other browsers.
The onchange event isn't fired if your textarea field holds the focus. Use onkeyup instead to execute the blockRefresh function immidiately.
var unfinished = false;
var timer = setTimeout(function(){
if(window.unfinished) {
alert("unfinished comment");
}
else {
window.location = window.location.pathname;
}
}, 6000);
function blockRefresh() {
window.unfinished = true;
console.log('Refresh blocked.');
}

Merging 2 javascripts into one

I have 2 javascripts working but want to merge them into one.
The purpose of the script is to make a checkbox that changes the a varable for the refresh rate.
Script 1 (works as expected)
window.onload = function () {
var input = document.querySelector('input[type=checkbox]');
function check() {
if (input.checked) {
var timeout = "10000";
} else {
var timeout = "999999";
}
document.getElementById('result').innerHTML = 'result ' + timeout;
}
input.onchange = check;
check();
}
<input type="checkbox" value="1" />Checkbox
<br/>
<br/>
<span id="result"></span>
Script 2 (works as expected)
var timeout = 10000;
var action = function() {
$('#content').load('/arduino/refresh');
};
setInterval(action, timeout);
I tought I could just merge them together and let them live happily ever after so I created the following script: (it shows the checkbox but checking/unchecking does not change the refresh rate)
window.onload = function () {
var input = document.querySelector('input[type=checkbox]');
function check() {
if (input.checked) {
var timeout = "10000";
} else {
var timeout = "999999";
}
var action = function() {
$('#content').load('/arduino/refresh');
};
}
input.onchange = check;
check();
}
setInterval(action, timeout);
<input type="checkbox" value="1" />Checkbox
<br/>
<br/>
<span id="result"></span>
Any advise?
In the combined script, setInterval is not called within the check event handler. The local variable timeout is modified, but the new value is never used to actually change the refresh rate.
Also, use clearInterval and setInterval together when changing the refresh rate.

Categories