Replaced timeout event fires twice or more (sometimes) - javascript

I have a section of content that allows a user to edit it upon double-clicking on it. If the user changes the content and then stops for 2 seconds, the updated content is sent to the server to be saved.
To do so, I have bound an input event listener to that section that starts a 2 seconds countdown, and if there is already a countdown, the former will be cancelled and a new one will start instead. At the end of the countdown an http POST request is sent to the server with the new data.
The problem is that sometimes at the end of the countdown I see 2 or more requests sent, as if a countdown was not cancelled before a new one was inserted, and I can't figure out why.
The code in question is as follows:
//this function is bound to a double-click event on an element
function makeEditable(elem, attr) {
//holder for the timeout promise
var toSaveTimeout = undefined;
elem.attr("contentEditable", "true");
elem.on("input", function () {
//if a countdown is already in place, cancel it
if(toSaveTimeout) {
//I am worried that sometimes this line is skipped from some reason
$timeout.cancel(toSaveTimeout);
}
toSaveTimeout = $timeout(function () {
//The following console line will sometimes appear twice in a row, only miliseconds apart
console.log("Sending a save. Time: " + Date.now());
$http({
url: "/",
method: "POST",
data: {
action: "edit_content",
section: attr.afeContentBox,
content: elem.html()
}
}).then(function (res) {
$rootScope.data = "Saved";
}, function (res) {
$rootScope.data = "Error while saving";
});
}, 2000);
});
//The following functions will stop the above behaviour if the user clicks anywhere else on the page
angular.element(document).on("click", function () {
unmakeEditable(elem);
angular.element(document).off("click");
elem.off("click");
});
elem.on("click", function (e) {
e.stopPropagation();
});
}

Turns out (with help from the commentators above) that the function makeEditable was called more than once.
Adding the following two lines of code at the beginning of the function fixed the issue:
//if element is already editable - ignore
if(elem.attr("contentEditable") === "true")
return;

Related

beforeinstallprompt triggers on every load

Beforeinstallprompt triggers on every load.
I have used the code here: https://developers.google.com/web/fundamentals/app-install-banners/
I am not using the The mini-info bar which i have dissabled by calling e.preventDefault();
The problem is that the showAddToHomeScreen(); is called on every load if the user does not click addToHomeScreen.
I want the showAddToHomeScreen(); function to be called only every month or so by storing information about the last "canceled" click in sessions or something similar. Isn't google suppose to do this on it's own?
This i found on the following link:
https://developers.google.com/web/updates/2018/06/a2hs-updates
You can only call prompt() on the deferred event once, if the user clicks cancel on the dialog, you'll need to wait until the beforeinstallprompt event is fired on the next page navigation. Unlike traditional permission requests, clicking cancel will not block future calls to prompt() because it call must be called within a user gesture.
window.addEventListener('beforeinstallprompt', function (e) {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
showAddToHomeScreen();
});
function showAddToHomeScreen() {
var prompt = document.querySelector(".a2hs-prompt");
prompt.style.display = "flex";
var open = document.querySelector(".a2hsBtn");
open.addEventListener("click", addToHomeScreen);
var close = document.querySelector(".a2hsBtn-close");
close.addEventListener("click", function() {
prompt.style.display = "none";
});
}
function addToHomeScreen() {
var prompt = document.querySelector(".a2hs-prompt");
// hide our user interface that shows our A2HS button
prompt.style.display = 'none';
if (deferredPrompt) {
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice.then(
function (choiceResult) {
if (choiceResult.outcome === 'accepted') {
show_ad2hs_success_message();
}
deferredPrompt = null;
});
}
}
You have to define your own session and add expire date. This is simple with ajax. This is how i did:
Javascript:
$(document).ready(function() {
$.ajax({
url: '/update_session_addtohomescreen',
success: function (session_expired) {
if(session_expired=='True'){
showAddToHomeScreen();
}
},
error: function () {
alert("it didn't work");
}
});
});
This is wrapping the showAddToHomeScreen(); function
View
#csrf_exempt
def update_session_addtohomescreen(request):
if request.is_ajax():
number_of_days_till_expire = 1
now_in_secs = time.time()
if not 'last_session_coockie' in request.session or now_in_secs > request.session['last_session_coockie']+60:#number_of_days_till_expire*86400:
session_expired = True
request.session['last_session_coockie'] = now_in_secs
else:
session_expired = False
return HttpResponse(session_expired)
return None
You should though include csrf token in your request and also add the url to urls.py

clearInterval and setInterval in javascript Instantly

<script>
Main Function:
var interval;
function refreshId(session_to_user) {
interval = setInterval(function()
{
$('.chat-box').load("<?php echo base_url()."users/message/refresh_div/"; ?>" + session_to_user);
}, 10000);
}
in this main function I'm only going to perform my requirement here. I have a variable interval my enabling this function, and it will refresh every 10 seconds.
onclick of function
forloop
{
<a href = "" onclick="myFunction(user_id)"
}
function myFunction(user_id){
clearInterval(interval);
$.ajax({
success:function(data)
{
$('.chats').html(data);
}
})
refreshId(session_to_user);
}
If anyone clicks on href, it should clearInterval if already exists else a new Interval has to be established on click function. In my current code, it works if I click for the first time. It starts refreshing the first link for every 10 seconds, but when I click for the second time on the second link, it waits. Still, the first one gets executed and then the second one is executing. My requirement is if I click, the established setInterval has to stopped instantly and the new one has to be started on the spot same as for my next function paper_plane() also.
function paper_plane()
{
clearInterval(interval);
$.ajax({
success:function(html)
{
$('#chat').append(html);
$('.chat-input').val('');
$('.chat-input').focus();
}
});
}
var side_bar_path = "<?php echo base_url()."users/message/load_side_bar"; ?>";
$.ajax({
success : function(data)
{
$('.msg-list').html(data);
}
});
refreshId(session_to_user);
}
There is no way you can cancel the ajax request after the delay(10 seconds) is elapsed from first click handler,since it would not return the result immediately.
so the only way you can cancel the previous ajax calls before making a new ajax call is to suppress/ignore the responses of the previous ajax calls once the second link is triggered, this way you will create a scenario what your expecting.
Below i have created a small snippet which will do the scenario mentioned above.
JS Code:
var firstTimer;
//first click handler
$('#first').on('click',function () {
firstTimer= setInterval(function (){
$.ajax({
url:"http://target.org/page.html",
dataType: "POST",
data:{'name1':'davy'}
success: function(data) {
//suppress the response when timer is stopped.
if(firstTimer>0){
return;
}
//if timer is not stopped process the data
},
error :function(error) {
//handle error
}
});
},1000);
});
//second click handler
$('#second').on('click',function () {
//clear the firstTimer before staring a new timer
clearInterval(firstTimer);
var timer;
timer = setInterval(function (){
$.ajax({
url:"http://target.org/page2.html",
dataType: "POST",
data:{'name1':'rog'}
success: function(data) {
//process the data
},
error :function(error) {
//handle error
}
});
},1000);
});

Ajax sends duplicate headers

Well I have stumbled upon a weird problem.
First time I send the form the server recieves 1. If I send another without reloading the page it sends 2 exact same posts, if I send it again it sends 3 and so on. If I would to send 9 forms it would send 9 exact same posts on the last.
Here's my code:
$('#AskACounsel .btn-ask-question').bind("click", function (e) {
e.preventDefault();
var textbox = $('#AskACounselQuestion');
var question = textbox.val();
var product = textbox.data('productid');
if (question.length > 0) {
var params = { question: question, productid: product };
var url = '/FAQService/AddQuestion';
$.ajax({
url: url, success: function (result) {
var infoDiv = $('#AskACounselThankYouView .counsel-info').html(result.Message);
var backDiv = $('#AskACounselThankYouView .counsel-footer .btn-return');
if (result.Success) {
textbox.val("");
backDiv.hide();
} else {
backDiv.show();
}
$('#AskACounselDefaultView').hide();
$('#AskACounselThankYouView').show();
},
type: 'POST',
data: params
});
}
});
Its likely that the code you have is passed through many times, i.e. you have a function that has the code and calling it to handle the click event. The rule of thumb is that binding should happen ONLY ONCE, since it's a binding, not just an event handler like "on".
See answer from https://stackoverflow.com/a/8065685/696034
If you want to continue using bind, then unbind before that code through $('#AskACounsel .btn-ask-question').unbind("click"); otherwise, bind the event in the initialization section of your code ONCE.

How do stop an alert from showing more than once on repeated calls to a function via AJAX?

Ok, I have this function below that I am calling every 5 seconds..
function checkVerfToken(user_id) {
// Set file to get results from..
var loadUrl = "ajax_files/check_verf_token.php";
// Set parameters
var dataObject = { user_id: user_id };
// Run request
getAjaxData(loadUrl, dataObject, 'POST', 'html')
.done(function(response) {
if (response == 'success') {
// Reload the page
window.location.replace('payment_completed_verf.php');
}
})
.fail(function() {
alert('There seems to have been a problem with continuing the verification process; please contact us if this continues to occur.');
});
// End
}
I am calling it from a page every 5 seconds with:
<script type="text/javascript">
window.setInterval(function(){
checkVerfToken(<?=$user_id?>);
}, 5000);
</script>
However if fail is executed then it obviously keeps doing it every 5 seconds as it doesn't leave the page.
How can I get it to just show the alert once if there is a problem?
Keep a reference to the interval, and clear it when it fails.
verifyInterval = window.setInterval(function(){
checkVerfToken(<?=$user_id?>);
}, 5000);
.fail(function() {
clearInterval(verifyInterval);
alert('There seems to have been a problem with continuing the verification process; please contact us if this continues to occur.');
});
Not sure if this is the best way, but creating a dom element on the fly and checking for its existence will work, you can even check for its value and change them for any other action; so basically it acts as a flag and if required can be a conditional checker for other event firing:
.fail(function() {
// create a hidden element in dom to check for the alert event execution
if ($('#errorchecker').length === 0) {
$('body').append('<input type="hidden" id="errorchecker" value="executed" />');
alert('There seems to have been a problem with continuing the verification process; please contact us if this continues to occur.');
}
});

weird problem - change event fires only under debug in IE

I have form autocomplete code that executes when value changes in one textbox. It looks like this:
$('#myTextBoxId)').change(function () {
var caller = $(this);
var ajaxurl = '#Url.Action("Autocomplete", "Ajax")';
var postData = { myvalue: $(caller).val() }
executeAfterCurrentAjax(function () {
//alert("executing after ajax");
if ($(caller).valid()) {
//alert("field is valid");
$.ajax({ type: 'POST',
url: ajaxurl,
data: postData,
success: function (data) {
//some code that handles ajax call result to update form
}
});
}
});
});
As this form field (myTextBoxId) has remote validator, I have made this function:
function executeAfterCurrentAjax(callback) {
if (ajaxCounter > 0) {
setTimeout(function () { executeAfterCurrentAjax(callback); }, 100);
}
else {
callback();
}
}
This function enables me to execute this autocomplete call after remote validation has ended, resulting in autocomplete only when textbox has valid value. ajaxCounter variable is global, and its value is set in global ajax events:
$(document).ajaxStart(function () {
ajaxCounter++;
});
$(document).ajaxComplete(function () {
ajaxCounter--;
if (ajaxCounter <= 0) {
ajaxCounter = 0;
}
});
My problem is in IE (9), and it occurs only when I normally use my form. Problem is that function body inside executeAfterCurrentAjax(function () {...}); sometimes does not execute for some reason. If I uncomment any of two alerts, everything works every time, but if not, ajax call is most of the time not made (I checked this by debugging on server). If I open developer tools and try to capture network or debug javascript everything works as it should.
It seems that problem occurs when field loses focus in the same moment when remote validation request is complete. What I think it happens then is callback function in executeAfterCurrentAjaxCall is executed immediately, and in that moment jquery validation response is not finished yet, so $(caller).valid() returns false. I still do not know how alert("field is valid") helps in that scenario, and that could be sign that I'm wrong and something else is happening. However, changing executeAfterCurrentAjaxCall so it looks like this seems to solve my problem:
function executeAfterCurrentAjax(callback) {
if (ajaxCounter > 0) {
setTimeout(function () { executeAfterCurrentAjax(callback); }, 100);
}
else {
setTimeout(callback, 10);
}
}

Categories

Resources