I am in a situation where I need to keep track of the previously clicked event.target.id that is firing the click event.
A very simple example of my code is as follows (I am using dojo and jQuery):
on(dom.byId("div-tools-draw"), "click", function (evt) {
var lastActiveTool = evt.target.id;
}
This code keeps overwriting the lastActiveTool variable with the current event id. However, I need a way to keep track of the previous one.
Sorry if this is a silly question, I am still learning JS.
var lastActiveTool;
on(dom.byId("div-tools-draw"), "click", function (evt) {
//do whatever you want with previous value if there is one
lastActiveTool = evt.target.id;
}
Firstly you shouldn't declare your variable inside the function as it'll only be accessible inside that function and since it's an anonymous function, it'll be destroyed each time the function is done running.
var lastActiveTool;
on(dom.byId("div-tools-draw"), "click", function (evt) {
if(typeof lastActiveTool !== 'undefined'){
//Do what you need to do with the last id. Add an else if you want something special to happen when the first element is clicked and there is no previous id.
}
lastActiveTool = evt.target.id;
}
Related
I was working today at a front-end Javascript project. I will try to keep the description of the problem and the solution as short as possible.
I had to add click handlers to the links on a page that redirect the user to other pages, so I had 2 Javascript array arrayOfRedirectLinks and pageLinkElements:
var arrayOfRedirectLinks, pageLinkElements;
Initially I wrote the addEventHandlers function like this:
var addEventHandlers = function() {
var i, link;
for( var i in arrayOfRedirectLinks) {
link = arrayOfRedirectLinks[i];
pageLinkElements[i].addEventListener('click', function(e) {
e.preventDefault();
window.location = link;
});
}
}
I thought that this solution will do the job until... well until I opened the browser, clicked several links and noticed that all of them redirected me to the same link (the last link in the arrayOfRedirectLinks).
Finally I found that my problem was similar to the one posted here
Javascript multiple dynamic addEventListener created in for loop - passing parameters not working
And indeed both the first and the second solution posted there worked for me
var addEventHandlers = function() {
var i, link;
for( var i in arrayOfRedirectLinks) {
(function(link){
link = arrayOfRedirectLinks[i];
pageLinkElements[i].addEventListener('click', function(e) {
e.preventDefault();
window.location = link;
});
}(link));
}
}
and
var passLink = function(link) {
return function(e) {
e.preventDefault();
window.location = link;
};
};
var addEventHandlers = function() {
var i, link;
for( var i in arrayOfRedirectLinks) {
link = arrayOfRedirectLinks[i];
pageLinkElements[i].addEventListener('click',passLink(link));
}
}
Now this seems to work but I don't understand why it works.
I came with the following explanation and I would like if someone can confirm if it's correct:
When I declare a function in Javascript, it gets the references to the variables in the scope of the function where it was declared. ( i.e. my event handler gets a reference to the link variable in the addEventHandlers function)
Because the handler gets a reference to the variable link. When I reassign a value to the link variable, the value that will be used when the click handler gets triggered will also change. So the link variable from the event handler is not simply copy with of the link with a different memory address and same value as when the function handler was added, but they both share the same memory address and therefore the same value.
Because of the reasons described at 2), the all the click handlers will use the redirect to the same link, the last link in the array arrayOfRedirectLinks because that's the last value that will get assigned to the link variable at the end of the for loop.
But when I pass the link variable as a parameter to another function, a new scope it's created and the link inside that scope actually shares only it's initial value with the value of the link parameter passed to the function. The references of the 2 link variables are different.
Because of 4), when I pass the link to the click handler, it will take the reference to the link variable in the Immediately Invoked Function Expression who itself doesn't share the same address with the link in the addEventHandlers function. Therefore each link from the event handler functions will be isolated from the others and will keep the value of the arrayOfRedirectLinks[i]
Is this correct?
This is the critical bit:
var i, link;
for( var i in arrayOfRedirectLinks) {
link = arrayOfRedirectLinks[i];
pageLinkElements[i].addEventListener('click', function(e) {
e.preventDefault();
window.location = link;
});
}
There is only ever one link variable here. Note that the addEventListener callback function is only called when the link is clicked.
By that time, the variable link has its final value, which is shared by all event handler functions.
So all the links do the same thing.
Simplest solution (other than a wider refactor):
for(var i in arrayOfRedirectLinks) {
(function(i) {
var link = arrayOfRedirectLinks[i];
pageLinkElements[i].addEventListener('click', function(e) {
e.preventDefault();
window.location = link;
});
}(i));
}
I can't seem to access the variable defaultValue down in my .blur() function. I've tried various stuff but with no luck. So far I only get an empty object. What's wrong?
jQuery(document).ready(function(){
jQuery('#nameInput, #emailInput, #webInput').focus(function(){
var defaultValue = jQuery(this).val();
jQuery(this).val("");
})
.blur(function(defaultValue){
if(jQuery(this).val() == ""){
jQuery(this).val(defaultValue);
}
});
});
Looks like the question is about the passing data into the .blur or .focus event.
per jQuery API - http://api.jquery.com/blur/
blur( [eventData ], handler(eventObject) )
So if you want to pass data - you can send a parameter to event - which will appear as data in event object.
see this fiddle for more info
http://jsfiddle.net/dekajp/CgP2X/1/
var p = {
mydata:'my data'
};
/* p could be element or whatever */
$("#tb2").blur(p,function (e){
alert('data :'+e.data.mydata);
});
Because your code is wrong :-) you define var inside function (var defaultValue) which is then immediately wiped out.
There are two solutions: define your var as a global var before you bind blur event, or store it in the data of object liket his (which I recommend):
$(document).ready(function(){
$('#nameInput, #emailInput, #webInput').focus(function(){
$(this).val("").data('defaultValue',jQuery(this).val());
}).blur(function(defaultValue){
if($(this).val() == ""){
$(this).val($(this).data('defaultValue'));
}
});
});
It seems to me that you don't understand the basics of JavaScript.
First of all variables in JS are localized to function's scope, so you can't declare variable with var in one function and access it in other function
Second, you can't pass anything to DOM-event handler, except event-object, this is defined by the DOM specification, sometimes you can use event data parameter to the blur jQuery method.
Try this:
jQuery(document).ready(function(){
var defaultValue;
jQuery("#nameInput, #emailInput, #webInput").focus(function(){
defaultValue = jQuery(this).val();
jQuery(this).val("");
})
.blur(function(){
if(jQuery(this).val() == ""){
jQuery(this).val(defaultValue);
}
});
});
First of all, you need to distinguish blur method (function) and handler (function) which is the argument to the blur. You was trying to pass the defaultValue exactly to handler, but that can't be done. Inside handler the defaultValue would be equal eventObject, so you can do smth like console.log(defaultValue.timeStamp) and you'll see smth like 123482359734536
In your approach you can't even use event.data argument to the blur cause it will be set at the time of blur's call (attaching handler). You need to declare a var outside of the both handlers, so it will be visible to both of them
You may consider to read some comprehensive book on JS.
I read "Professional JaveScript For Webdevelopers" by Nicolas Zakas. There is a new edition
So I have a group of events like this:
$('#slider-1').click(function(event){
switchBanners(1, true);
});
$('#slider-2').click(function(event){
switchBanners(2, true);
});
$('#slider-3').click(function(event){
switchBanners(3, true);
});
$('#slider-4').click(function(event){
switchBanners(4, true);
});
$('#slider-5').click(function(event){
switchBanners(5, true);
});
And I wanted to run them through a loop I am already running something like this:
for(i = 1; i <= totalBanners; i++){
$('#slider-' + i).click(function(event){
switchBanners(i, true);
});
}
In theory that should work, but it doesnt seem to once I load the document... It doesnt respond to any specific div id like it should when clicked... it progresses through each div regardless of which one I click. There are more event listeners I want to dynamically create on the fly but I need these first...
What am I missing?
This is a very common issue people encounter.
JavaScript doesn't have block scope, just function scope. So each function you create in the loop is being created in the same variable environment, and as such they're all referencing the same i variable.
To scope a variable in a new variable environment, you need to invoke a function that has a variable (or function parameter) that references the value you want to retain.
In the code below, we reference it with the function parameter j.
// Invoke generate_handler() during the loop. It will return a function that
// has access to its vars/params.
function generate_handler( j ) {
return function(event) {
switchBanners(j, true);
};
}
for(var i = 1; i <= totalBanners; i++){
$('#slider-' + i).click( generate_handler( i ) );
}
Here we invoked the generate_handler() function, passed in i, and had generate_handler() return a function that references the local variable (named j in the function, though you could name it i as well).
The variable environment of the returned function will exist as long as the function exists, so it will continue to have reference to any variables that existed in the environment when/where it was created.
UPDATE: Added var before i to be sure it is declared properly.
Instead of doing something this .. emm .. reckless, you should attach a single event listener and catch events us they bubble up. Its called "event delegation".
Some links:
http://davidwalsh.name/event-delegate
http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-event-delegation-in-4-minutes/
http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/
http://lab.distilldesign.com/event-delegation/
Study this. It is a quite important thing to learn about event management in javascript.
[edit: saw this answer get an upvote and recognized it's using old syntax. Here's some updated syntax, using jQuery's "on" event binding method. The same principle applies. You bind to the closest non-destroyed parent, listening for clicks ON the specified selector.]
$(function() {
$('.someAncestor').on('click', '.slider', function(e) {
// code to do stuff on clicking the slider. 'e' passed in is the event
});
});
Note: if your chain of initialization already has an appropriate spot to insert the listener (ie. you already have a document ready or onload function) you don't need to wrap it in this sample's $(function(){}) method. You would just put the $('.someAncestor')... part at that appropriate spot.
Original answer maintained for more thorough explanation and legacy sample code:
I'm with tereško : delegating events is more powerful than doing each click "on demand" as it were. Easiest way to access the whole group of slider elements is to give each a shared class. Let's say, "slider" Then you can delegate a universal event to all ".slider" elements:
$(function() {
$('body').delegate('.slider', 'click', function() {
var sliderSplit = this.id.split('-'); // split the string at the hyphen
switchBanners(parseInt(sliderSplit[1]), true); // after the split, the number is found in index 1
});
});
Liddle Fiddle: http://jsfiddle.net/2KrEk/
I'm delegating to "body" only because I don't know your HTML structure. Ideally you will delegate to the closest parent of all sliders that you know is not going to be destroyed by other DOM manipulations. Often ome sort of wrapper or container div.
It's because i isn't evaluated until the click function is called, by which time the loop has finished running and i is at it's max (or worse overwritten somewhere else in code).
Try this:
for(i = 1; i <= totalBanners; i++){
$('#slider-' + i).click(function(event){
switchBanners($(this).attr('id').replace('slider-', ''), true);
});
}
That way you're getting the number from the id of the element that's actually been clicked.
Use jQuery $.each
$.each(bannersArray, function(index, element) {
index += 1; // start from 0
$('#slider-' + index).click(function(event){
switchBanners(index, true);
});
});
You can study JavaScript Clousure, hope it helps
I have a contact form that sends a value to a hidden input on successful completion of the sendmail function. I want to detect this value change and then use it to apply a class to a div/paragraph.
I asked a similar question recently and I'm aware that this requires the script to continually check the doc after DOM is loaded but even after adding .change() it just doesn't seem to want to add the class.
Here's the jQuery:
$(document).ready(function() {
$("#acf_success_sent").change(function(){
if ($("#acf_success_sent").val() == "1"){
$("#acf_verified").addClass('gone');
}
});
});
any help would be great. here's a link to a test version of form in case you're interested, everything works except the verified symbol doesn't disappear after a successful send http://seeshell.me/forms/contact.php
There'll be no "change" event fired when code updates the value of your <input> element, so the handler you've registered won't run. What you could do however is fire "change" from a watchdog:
var watchdog = setInterval(function() {
if ($('#acf_success_sent').val() !== originalValue)
$('#acf_success_sent').trigger('change');
}, 100);
How you set up "originalValue" depends on your application. You could, for example, keep a separate ".data()" value, and watch for whenever your saved value differs from the current "value" attribute. Or you could keep the value in a closure variable:
var watchdog = (function() {
var $acfSuccessSent = $('#acf_success_sent'), cachedValue = $acfSuccessSent.val();
return function() {
if (cachedValue !== $acfSuccessSent.val())
$acfSuccessSent.trigger('change');
};
})();
I need my script to do something on the first time an element is clicked and continue to do something different on click 2,3,4 and so on
$('selector').click(function() {
//I would realy like this variable to be updated
var click = 0;
if (click === 0) {
do this
var click = 1;
} else {
do this
}
});//end click
really I think it should rely on the variables but I can't think of how to update the variable from here on out any help would be awesome.
Have a look at jQuery's .data() method. Consider your example:
$('selector').click(function() {
var $this = $(this),
clickNum = $this.data('clickNum');
if (!clickNum) clickNum = 1;
alert(clickNum);
$this.data('clickNum', ++clickNum);
});
See a working example here: http://jsfiddle.net/uaaft/
Use data to persist your state with the element.
In your click handler,
use
$(this).data('number_of_clicks')
to retrieve the value and
$(this).data('number_of_clicks',some_value)
to set it.
Note: $(this).data('number_of_clicks') will return false if it hasn't been set yet
Edit: fixed link
Another alternative might be to have two functions, and bind one using the one function in $(document).ready() (or wherever you are binding your handlers), and in that function, bind the second function to be run for all subsequent clicks using bind or click.
e.g.
function FirstTime(element) {
// do stuff the first time round here
$(element.target).click(AllOtherTimes);
}
function AllOtherTimes(element) {
// do stuff all subsequent times here
}
$(function() {
$('selector').one('click', FirstTime);
});
This is super easy in vanilla Js. This is using proper, different click handlers
const onNextTimes = function(e) {
// Do this after all but first click
};
node.addEventListener("click", function onFirstTime(e) {
node.addEventListener("click", onNextTimes);
}, {once : true});
Documentation, CanIUse
If you just need sequences of fixed behaviors, you can do this:
$('selector').toggle(function(){...}, function(){...}, function(){...},...);
Event handlers in the toggle method will be called orderly.
$('#foo').one('click', function() {
alert('This will be displayed only once.');
});
this would bind click event to Corresponding Html element once and unbind it automatically after first event rendering.
Or alternatively u could the following:
$("#foo").bind('click',function(){
// Some activity
$("#foo").unbind("click");
// bind it to some other event handler.
});