JavaScript Callback being kept in memory and excuted with new callback - javascript

I'm working on a CMS type project using ajax. My problem occurs with the notify.js javascript library located here https://notifyjs.com/.
With my local copy I realized that I can create confirm buttons sleekly designed without using Javascript's built in alert() or confirm() methods and make it look like this:
.
Seeing that the source code provided no way to explicitly add callbacks from outside the library I modified it to my will, where the "callback" parameter is my custom parameter
I.E.
//confirmation notification
Notification.prototype.confirm = function (style, position, title, text, callback) {
var icon = "fa fa-adjust";
if (style == "error") {
icon = "fa fa-exclamation";
} else if (style == "warning") {
icon = "fa fa-warning";
} else if (style == "success") {
icon = "fa fa-check";
} else if (style == "info") {
icon = "fa fa-question";
} else {
icon = "fa fa-adjust";
}
$.notify({
title: title,
text : text + '<div class="clearfix"></div><br><a id="yesConfirmBtn" class="btn btn-sm btn-default yes">Yes</a><a id="noConfirmBtn" class="btn btn-sm btn-danger no">No</a>',
image: "<i class='" + icon + "'></i>"
}, {
style : 'metro',
className : style,
globalPosition: position,
showAnimation : "show",
showDuration : 0,
hideDuration : 0,
autoHide : false,
clickToHide : false
});
//listen for click events from this style
$(document).on('click', '.notifyjs-metro-base .no', function () {
// turn off event listener on yes click
$(document).off("click", '.notifyjs-metro-base .yes');
//programmatically trigger propogating hide event
$(this).trigger('notify-hide');
});
var yesClick = function () {
//callback when user clicks
callback();
// callback is assigned empty closure because on
// the next confirm request this callback and
// the new one will be called
callback = function(){
};
//hide notification
$(this).trigger('notify-hide');
};
$(document).on('click', '.notifyjs-metro-base .yes', yesClick);
}
In the click listeners my problem occurs the yesClick listener mainly as it continue to add the callbacks one on top the other so each time a callback is created and triggered the old one is triggered then the rest till the newest callback.
To by pass this problem I made the callback point to a empty anonymous function so when the old one's are executed it wouldn't do anything but this way still doesn't solve my problem as the callbacks will still be in memory.
The callback passed in performs an Ajax request to delete a row in the database.
This is what the callback looks like
var self = this;
// triggers notify.js confirmDelete() method passing in the
// body, title, and callback
this.notif.confirmDelete("Are you sure?", "Delete this Item?", function () {
// creates a notification to show user their
// transaction is being processed
self.notif.info("Deleting");
// callback for if the ajax request was successful
var successCB = function (text) {
if (text.indexOf("true") !== -1) {
self.notif.success("Item has been deleted", "Successfully Deleted");
var form = new Validate(),
table = new Table();
form.resetForm();
table.removeRow(data.id);
} else {
self.notif.error("Could not be deleted...");
}
// callback if ajax request failed
}, failCB = function () {
// this key word refer's to a method called
//Success which is the same as successCB as they
//are placed in the same object literal
this.Success("false");
};
self.ajaxRequest(successCB, failCB, data);
});
self.ajaxRequest looks like this (this is a custom ajax library I made if it seems strange)
var self = this;
var dataToProcess = data || {};
// basic set up for ajax request
Ajaxify({
host : self.hostName + "Backend/manage/" + self.processFile,
data : [dataToProcess],
responseType: "text",
method : "POST",
execute : true,// executes request right away
Success : successCallback,
Failure : failureCallback
});
What am I doing wrong?
How can I solve this?
What is the cause of it?
(Note: if any further info is needed let me know)

You need to remove click events with off, because you're adding global click with on
function noClick() {
//programmatically trigger propogating hide event
$(this).trigger('notify-hide');
removeEvents();
}
function removeEvents() {
$(document).off('click', '.notifyjs-metro-base .no', noClick);
$(document).off('click', '.notifyjs-metro-base .yes', yesClick);
}
$(document).on('click', '.notifyjs-metro-base .no', no);
function yesClick () {
//callback when user clicks
callback();
//hide notification
$(this).trigger('notify-hide');
removeEvents();
};
$(document).on('click', '.notifyjs-metro-base .yes', yesClick);

Related

Javascript- How to check if operation has been completed on this event

Is there any way to check if the event is completed and element is free to perform another action?
Like I want to do
$('#button-cancel').on('click', function() {
// send ajax call
});
/****************************************
extra code
*******************************************/
$('#button-cancel').on('click', function() {
if(ajax call is completed) {
//do some thing
}
});
I don't want to send ajax call in second onclick as it is already been sent, just want to check if it is done with ajax then do this
You can introduce a helper variable:
// introduce variable
var wasAjaxRun = false;
$('#button-cancel').on('click', function() {
// in ajax complete event you change the value of variable:
$.ajax({
url: "yoururl"
// other parameters
}).done(function() {
// your other handling logic
wasAjaxRun = true;
});
});
$('#button-cancel').on('click', function() {
if(wasAjaxRun === true) {
//do some thing
}
});
EDIT: I just noticed that you have event handlers attached to the same button. In that case my initial answer would not work, because first event hander would be executed every time you click the button.
It is not very clear from the description what you want to do with your first event hander. I assume you want to use some data, and if you already have this data, then you use it immediately (like in second handler), if you don't have it - you make the AJAX call to get the data (like in first handler).
For such scenario you could use single event handler with some conditions:
var isAjaxRunning = false; // true only if AJAX call is in progress
var dataYouNeed; // stores the data that you need
$('#button-cancel').on('click', function() {
if(isAjaxRunning){
return; // if AJAX is in progress there is nothing we can do
}
// check if you already have the data, this assumes you data cannot be falsey
if(dataYouNeed){
// You already have the data
// perform the logic you had in your second event handler
}
else { // no data, you need to get it using AJAX
isAjaxRunning = true; // set the flag to prevent multiple AJAX calls
$.ajax({
url: "yoururl"
}).done(function(result) {
dataYouNeed = result;
}).always(function(){
isAjaxRunning = false;
});
}
});
You should be able to provide handlers for AJAX return codes. e.g
$.ajax({
type: "post", url: "/SomeController/SomeAction",
success: function (data, text) {
//...
},
error: function (request, status, error) {
alert(request.responseText);
}
});
you can disable the button as soon as it enters in to the event and enable it back in ajax success or error method
$('#button-cancel').on('click', function() {
// Disable button
if(ajax call is completed) {
//do some thing
//enable it back
}
});
This is edited, more complete version of dotnetums's answer, which looks like will only work once..
// introduce variable
var ajaxIsRunning = false;
$('#button').on('click', function() {
// check state of variable, if running quit.
if(ajaxIsRunning) return al("please wait, ajax is running..");
// Else mark it to true
ajaxIsRunning = true;
// in ajax complete event you change the value of variable:
$.ajax({
url: "yoururl"
}).done(function() {
// Set it back to false so the button can be used again
ajaxIsRunning = false;
});
});
You just need to set a flag that indicates ajax call is underway, then clear it when ajax call returns.
var ajaxProcessing = false;
$('#button-cancel').on('click', function(){
processAjaxCall();
});
function processAjaxCall() {
if(ajaxProcessing) return;
ajaxProcessing = true; //set the flag
$.ajax({
url: 'http://stackoverflow.com/questions/36506931/javascript-how-to-check-if-operation-has-been-completed-on-this-event'
})
.done(function(resp){
//do something
alert('success');
})
.fail(function(){
//handle error
alert('error');
})
.always(function(){
ajaxprocessing = false; //clear the flag
})
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button-cancel">Cancel</button>
What you can do is call a function at the end of an if statement like
if(ajax call is completed) {
checkDone();
}
function checkDone() {
alert("Done");
}

Custom Confirmation dialog doesn't work properly

I have created custom dialogs like Alert and ConfirmDialog with Bootstrap and Jquery.
Here is sample : http://jsfiddle.net/eb71eaya/
Problem - in the callback I make an ajax call, and if it returns true - I show an alert with status success else - error. But this alert doesn't show while request makes delete. (In the example I don't make an ajax request, just show alert, but that also doesn't work.)
function getEsrbAlertDialog(title, msg, callBack, obj) {
var esrbAlertDialog = $('#esrb-dialog');
if (esrbAlertDialog.length == 0) {
esrbAlertDialog = getEsrbDialog(title, msg);
} else {
$('.modal-title', esrbAlertDialog).html(title);
$('.modal-body', esrbAlertDialog).html(msg);
}
$('.modal-footer div', esrbAlertDialog).empty();
$('.modal-footer div', esrbAlertDialog).append('<button class="btn btn-primary pull-right close-btn">Close</button>');
$('.close-btn', esrbAlertDialog).unbind('click').click(function () {
if (typeof callBack === "function") {
todo = callBack(obj);
}
esrbAlertDialog.modal('hide');
});
return esrbAlertDialog;
};
I want to execute callback when confirmation dialog become closed.
UPDATE : I understand logic like this : When user click on "Ok"-button, dialog must be closed. And when it is already closed then fire event 'hidden.bs.modal' which must execute callBack. But CallBack executes before confirm dialog finish hiding.
This line:
esrbConfirmationDialog.modal('hide');
Is hiding the second dialog.
EDIT:
Both dialogs use the same div as there reference:
var esrbAlertDialog = $('#esrb-dialog');
Create seperate dialogs one for the alert and one for confirmation.
Just replace this.Alert function to below code, i.e. just add e.preventDefault();
this.Alert = function (dialogMsg, callBack, obj) {
var dlg = getEsrbAlertDialog('Alert', dialogMsg, callBack, obj);
e.preventDefault();
dlg.modal('show');
};

re-execute javascript after log-in

I have a series of buttons that execute different functions when clicked. The function checks whether the user is logged in, and if so proceeds, if not it displays an overlay with ability to log in/create account.
What I want to do is re-execute the button click after log-in, without the user having to reclick it.
I have it working at the moment, but I'm pretty sure that what I'm doing isn't best practice, so looking for advice on how I can improve...
Here's what I'm doing: setting a global variable "pending_request" that stores the function to be re-run and in the success part of the log-in ajax request calling "eval(pending_request)"
Example of one of the buttons:
jQuery('#maybe_button').click(function() {
pending_request = "jQuery('#maybe_button').click()"
var loggedin = get_login_status();
if (loggedin == true) {
rec_status("maybe");
}
});
.
success: function(data) {
if(data === "User not found"){
alert("Email or Password incorrect, please try again");
}else{
document.getElementById('loginscreen').style.display = 'none';
document.getElementById('locationover').style.display = 'none';
eval(pending_request);
pending_request = "";
}
}
Register a function to handle the click and then invoke that func directly without eval().
jQuery('#maybe_button').on('click', myFunction)
This executes myFunction when the button is clicked. Now you can "re-run" the function code every time you need it with myFunction().
And btw since you are using jQuery you can do $('#loginscreen').hide() where $ is an alias for jQuery that's auto defined.
EDIT
Please, take a look at the following code:
var pressedButton = null;
$('button1').on('click', function() {
if (!isLoggedIn()) {
pressedButton = $(this);
return;
}
// ...
});
And, in your success handler:
success: function() {
// ...
if (pressedButton) pressedButton.trigger('click');
// ...
}

Listen for a function to be fired?

this is more of an academic question than a problem I'm having.
I've written a function that sets up a click handler for Google Analytics Event Tracking. It starts off by removing any previous click handlers using a namespace so as not to interfere with other click handlers that may be applied. Using jQuery's $.each() it basically loops through the IDs in an array, applies the category,action and label as data-* attributes and a class to reference it by later.
It then uses the data attributes set previously to set up a click handler which in turn fires the Google Analytics Event Tracking.
My question is basically, can I bind this function to an .unbind() or .off() method that targets all click handlers? (not namespaced ones)
So if someone else unbinds all click handlers on a button that the Event Tracking code is applied to, the setupEventHandlers() function is run automatically and reapplies the lost GA Event Tracking code?
UPDATED QUESTION
My original question might have been unclear in what I was trying to achieve.
So there is a button in the DOM.
<input type='submit' value='Confirm' id='confirm-btn'>
setupEventHandlers() runs as soon as the DOM is ready, and applies an event listener to #confirm-btn, which is listening for the click event. Another function randomFunction() decides that it needs to remove all event listeners from #confirm-btn, using the $.off() method. I would like to be able to allow randomFunction() to remove all event listeners, as it may not have a namespace (for reason x), and then run setupEventHandlers() again once $.off() has finished.
Consider that I can't directly edit the HTML because it's used across multiple sites, and the tracking code is for a single instance.
function setupEventHandlers(){
var buttonsToTrack = [['#buttonId','category','action', 'label'],
['#buttonId2','category2','action2', 'label2'],
['#buttonId3','category3','action3', 'label3']]
$('.js-buttonTracker').off('click.eventTrackingHandler');
$.each(buttonsToTrack, function(index, buttonDetails){
if(document.querySelector(buttonDetails[0]) != null){
$(buttonDetails[0]).attr('data-category', buttonDetails[1]);
$(buttonDetails[0]).attr('data-action', buttonDetails[2]);
$(buttonDetails[0]).attr('data-label', buttonDetails[3]);
$(buttonDetails[0]).addClass('js-buttonTracker');
}
});
$('.js-buttonTracker').on('click.eventTrackingHandler', function(){
_gaq.push(['_trackEvent', $(this).attr('data-category'), $(this).attr('data-action'), $(this).attr('data-label')]);
console.log($(this).attr('data-category') + ' ' + $(this).attr('data-action'), $(this).attr('data-label'));
});
}
// Save the original .off in a variable
(function(jq_off, recursive) {
// Redefine .off
$.fn.off = function(event, selector, function) {
var save_recursive = recursive;
recursive = true;
// Then call the original $.off
var return_val = jq_off.apply(this, arguments);
recursive = save_recursive;
if (!recursive) {
setupEventHandlers();
}
return return_val;
};
)($.fn.off, false);
You don't need to redefine .unbind, as it just calls .off.
My advise to you is to use data- tag elements and assign them a click listener independent of other functionalities, for example :
HTML:
<a href="http://google.com" data-toggle="GAPush" data-event="_trackEvent" data-action="google"....>..</a>
JS:
$('[data-toggle="GAPush"]').click(function() {
var eventname = $(this).data('name');
var action = $(this).data('action');
//do your login here and read other parameters from data-eventname m use _gapush()
});
you know you can have unlimited data- tag elements if you need them for other functionalities.
Try (this pattern)
Edit
v2
It should be possible to utilize setInterval to "poll" for the event namespace being attached, or not , to the target elements
window.setInterval(function() {
$._data($(".js-buttonTracker")[0], "events") != undefined &&
$._data($(".js-buttonTracker")[0], "events")
.click[0].namespace === "eventTrackingHandler"
? $.noop()
: setupEventHandlers()
}, 1000);
v1
html
<button>1</button>
<button>2</button>
<button>3</button>
<!-- `data`, `em` for `event` status notifications at jsfiddle -->
<data></data>
<em></em>
js
$(function () {
setupEventHandlers = function (status) {
if (status === "on") {
// if `events` `off`, turn back `on` by calling `_events()`
_events();
$(window).trigger("mousemove.check");
} else {
$("data").html(status);
};
};
eventsCheck = function () {
$(window).on("mousemove.check", function (e) {
// `target` `elements` ;
// e.g., `.js-buttonTracker`
var el = $("button");
// if `events` attached to `el`,
// check `events` `namespace`
if ($._data($(el)[0], "events") != undefined) {
$.each($._data($(el)[0], "events").click, function (k, v) {
if (v.namespace != "eventTrackingHandler") {
setupEventHandlers("attach `eventTrackingHandler`"
+ " namespace called");
} else {
$("data").html("`eventTrackingHandler`"
+ " namespace attached");
};
});
} else {
// if `events` _not_ attached to `el`,
// turn `events` back `on` by calling
// `setupEventHandlers("on")`
$("data").html("`eventTrackingHandler` namespace `off` ,"
+ " turning back `on` in 1 seconds");
// `setTimeout()` utilized here to show operability,
// alternatively, could call `setupEventHandlers("on")`
// without delay
setTimeout(function () {
setupEventHandlers("on");
}, 1000);
};
});
};
eventsCheck();
_events = function () {
$("button").on("click.eventTrackingHandler", function (e) {
$("em").html("clicked at " + $.now());
// turn `off` `events` for `button`'s when `button`
// having `text` of `"3"` `click`ed,
// simulating the condition
// "if someone else unbinds all click handlers on a button
// that the Event Tracking code is applied to" ,
// "observed" by `eventsCheck()` ,
// which calls `_events()` within `setTimeout()`
if ($(e.target).text() === "3") {
$("button").off("click.eventTrackingHandler");
};
});
};
_events();
});
jsfiddle http://jsfiddle.net/guest271314/z5u97/

jquery get variable from another function

I am using jquery raty rating plugin and would like to grab the score on click and pass it to to the second function which will send it via ajax to php to insert in to database along with a feedback. can someone help me out?
$(function() {
$('.rate').raty({
click: function(score, evt) {
var rate = score;//this is the variable I want to pass
},
path : '../img',
size : 24,
half :true,
cancel : true,
cancelOff : 'cancel-off-big.png',
cancelOn : 'cancel-on-big.png',
starHalf : 'star-half-big.png',
starOff : 'star-off-big.png',
starOn : 'star-on-big.png',
starOn : 'star-on-big.png',
score: function() {
return $(this).attr('data-score');
}
});
//the below function is in included js file
$(function() {
$("#submit" ).click(function() {
//how can I access the above var here?
if(rate == "")
{
$(".error").html("Score is missing");
}
else{
$.ajax({
//send data
});
}
});
});
Prefixing a variable with var makes it accessible only within that scope. By doing this, you're only making it available inside the click handler for .rate.
Instead, make the scope wider;
// define here, in a scope both functions can access!
var rate = null;
$(function () {
$('.rate').raty({
click: function (score, evt) {
rate = score;
},
path: '../img',
size: 24,
half: true,
cancel: true,
cancelOff: 'cancel-off-big.png',
cancelOn: 'cancel-on-big.png',
starHalf: 'star-half-big.png',
starOff: 'star-off-big.png',
starOn: 'star-on-big.png',
starOn: 'star-on-big.png',
score: function () {
return $(this).attr('data-score');
}
});
//the below function is in included js file
$(function () {
$("#submit").click(function () {
if (rate == null) {
$(".error").html("Score is missing");
} else {
$.ajax({
//send data
});
}
});
});
You'll notice I've changed your rate == "" to rate == null; I've looked at the raty docs, and the only valid values I could find for the click handler is either a number, or null (in the event "cancel" is clicked). So the comparison against null will work for both the user not choosing a value, or the user clearing the value (if you have that setting enabled).
Variable rate is defined in callback function. If it's defined inside of function it means it's local variable and it's valid and exist only in this function. You must define this variable out of this function.
If you can't do so - becouse of external file with jQuery Raty code which you can't edit - so in such case - I think the only one possibility is to read DOM and take value of hidden input which is generated by jQuery Raty.
But input doesn't fire change event when the value is programmatically changed so I have no idea how to detect that user clicked rating icon .......

Categories