I have this code in JavaScript:
status = document.getElementById('status2');
$('#slider > img').hover(
function() {
stopLoop();
status.innerHTML = "paused";
},
function() {
startSlider();
status.innerHTML = "playing";
}
);
where I look for all the images in my html that have the id slider and when I hover on then I want to add a word (paused or playing) to a span tag that has the id status2. But I don't know why the global variable is not working, the only way that I make it work is putting a local variable inside each funcion like this:
function() {
stopLoop();
var status = document.getElementById('status2');
status.innerHTML = "paused";
},
function() {
startSlider();
var status = document.getElementById('status2');
status.innerHTML = "playing";
}
Can anyone me why?
NOTE: as I said before all works with the local variables but not setting it as a global variable.
Because by the time you run
status = document.getElementById('status2');
DOM was not ready so you get status as undefined and so it wont work further.
So either put the code in ready
$(document).ready(function(){
//code goes here
})
or
Put the script at the end of file.
Do add in a
$(document).ready(function(){
});
This waits to execute the code inside until everything has finished loading. That way it shouldn't return undefined.
ALSO
I couldn't help but noticing that you seem to be trying to give multiple items the same ID.
Don't use IDs for multiple elements. That's not how they are designed to be used, nor do they work that way.If you give multiple elements the same ID and then try and style them with CSS, it'll only style the first one. Use a class. If you use
document.getElementById();
to try and grab multiple elements with the same ID, then the script will ONLY grab the FIRST element with that ID, because, given that it is an ID, it expects only one element. If you want to work with multiple elements, use a class, and then use
document.getElementsByClassName();
this will grab ALL elements with that class. So for example,
say you have four span elements with the class "foo". To grab all these and change the text, do this:
elements=document.getElementsByClassName("foo");
for (i=0; i<elements.length; i++){
elements[i].innerHTML='insert your text here';
}
About global and local variables, a GLOBAL variable is declared this way:
global_variable='foo'
and a local variable is declared this way:
var local_variable='foo'
a Global variable can be declared anywhere in the script and be used anywhere inside the script(and even in other scripts that are attached to the same HTML file ), whereas a Local variable, if declared inside the function, can only be used inside the function, or if you declare it outside the function, it can't be accessed within the function unless you pass the variable to it.
Hope that helps!
Related
I need to capture the selected user option and send that value in a post request.
Let's ignore the post part as it is not directly related to the question.
But right now the value appears as undefined.
CodePen:
https://codepen.io/ogonzales/pen/yLyQQaP
I've this JS, but lista_id, the value I need to capture, appears as undifined. Why?
I've tried to set this as a global variable. In the JS, you'll see an
alert, this alerts undefined for lista_id
JS:
<script>
$("#agregarProducto1").click(function () {
var lista_id;
$('#listas-de-usuario').change(function() {
var lista_id = $(this).find('option:selected').val();
});
alert(lista_id);
// $.post("{% url 'listas/agregar-producto/", {
// c_slug: "cuadernos",
// s_slug: "Cuadernos",
// product_slug: "cuadernos-rojos",
// lista_id: lista_id,
// });
});
</script>
You could declare the variable only once either globally or in the surrounding function and then not declare them inside the click or change handlers. This way, it is the same variable you are referring to.
Also, the code to bind change handler could be outside the click handler, otherwise it would be bound every time the button is clicked.
Example of global variable declaration:
CodePen
Example of variable declared inside document ready function of jQuery:
CodePen
I have a function that is run on a generic table, it can't be specific, it needs to be able to be run on many different tables. The problem I'm having is if it's run on multiple tables on the same page. The click events fire just fine, but they only operate on the check elements in the final table, not the check elements within the table they were supposed to work on.
Here's the code:
$parent = $table.parents('div.ibox').find('div.ibox-title');
$select_all = $parent.find('button.select_all');
$deselect_all = $parent.find('button.deselect_all');
$input_delete = $table.find('input.delete');
if ($table.find('input.delete').length >= 2) {
$select_all.removeClass('hidden');
} else {
$select_all.addClass('hidden');
$deselect_all.addClass('hidden');
}
$select_all.on('click', function(event) {
$input_delete.each(function() {
$(this).prop('checked', true).trigger('change');
$select_all.addClass('hidden');
$deselect_all.removeClass('hidden');
});
});
$deselect_all.on('click', function(e) {
$table.find('input.delete:checked').each(function() {
$(this).prop('checked', false).trigger('change');
$deselect_all.addClass('hidden');
$select_all.removeClass('hidden');
});
});
$table is a variable containing the $('table') element the function is being run on, it's passed into the function.
Just wondering if anyone has any ideas on how to get the click elements to fire on the checkboxes within the table they are supposed to, and not the final table on the page.
Assuming you haven't omitted any code from your function other than the actual function someFunc() { and closing } part, these lines:
$parent = $table.parents('div.ibox').find('div.ibox-title');
$select_all = $parent.find('button.select_all');
$deselect_all = $parent.find('button.deselect_all');
$input_delete = $table.find('input.delete');
...all declare (or update) global variables. So after the function has been run for several different tables, those variables will continue to reference the last table's elements.
Those variables should all be declared with var, making them local to your function. Then your click event handlers will be referring to their own variables and not sharing global variables. (The click handlers can continue to refer to local variables in their containing scope even after the containing function completes, because closures.)
I'll be short with words, here's the situation:
for (var _im = 0; _im < slideshow._preloadbulks[slideshow._preloadCurrentbulk].length; _im++) {
var tmpSlideIndex = (slideshow._preloadCurrentbulk*slideshow._preloadMaxbulkSize)+_im;
slideshow._preloadSlides[tmpSlideIndex] = document.createElement('video');
slideshow._preloadSlides[tmpSlideIndex].autoplay = false;
slideshow._preloadSlides[tmpSlideIndex].loop = false;
slideshow._preloadSlides[tmpSlideIndex].addEventListener('canplaythrough', slideshow.slideLoaded, false);
slideshow._preloadSlides[tmpSlideIndex].src = slideshow._slides[tmpSlideIndex][slideshow.image_size+"_video_url"];
slideshow._preloadSlides[tmpSlideIndex].addEventListener('error', function(){
console.log(tmpSlideIndex);
slideshow._preloadSlides.splice(tmpSlideIndex,1);
slideshow._slides.splice(tmpSlideIndex,1);
slideshow.slideLoaded();
}, true);
}
As you can see, I have a video array and I'm loading each element src to the DOM to pre-load it.
It works just fine, but I have to deal with a situation when one resource is n/a, then I need to remove it from the existing arrays.
The addEventListener('error', works just fine, it detects the unavailable resource but when I'm logging tmpSlideIndex into the console I get a different value rather than the original slide index (because the loop continues).
I've tried setting the useCapture flag as you can see to the error handler, thinking that will do the trick but it won't.
Any tricks?
Thanks!
The issue is that when you are creating a closure over the tmpSlideIndex variable, it allows you to reference that variable inside the children function, but it's not creating a brand new variable, and since the loop continues and your error handler function executes asynchronously, the value of tmpSlideIndex will always be the last index of the loop. To keep the original value, we can create a self-executing function to wich we will pass the value of tmpSlideIndex. That self-executing function will effectively create a new scope and we will finally return a function that will create a closure over the slideIndex variable that lives in it's parent function scope.
slideshow._preloadSlides[tmpSlideIndex].addEventListener('error', (function(slideIndex) {
return function () {
console.log(slideIndex);
slideshow._preloadSlides.splice(slideIndex,1);
slideshow._slides.splice(slideIndex,1);
slideshow.slideLoaded();
};
})(slideIndex), true);
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 want to create an element in place where javascript function run.
<script>
var uniqid='an unique Id';
document.write('<iframe id='+uniqid+'></iframe>');
someAsyncFunction(callback)
</script>
in other part of code I set additional parameters for this element
<script>
callback=function(paramfromcallback){
getElementById(uniqid).url=paramfromcallback;
}
</script>
but this is not work in IE. I can't find this element.
My problem is:
There are many elements on page, for each element must be set only this element parameters
I do not know which element is parent, I do not know the script element ID. I need insert an element in place where are first script called
First part of code executed before any call back, and I want to keep it asynchronous
Depending on the order your script blocks are hit, you could have a number of issues here. document.write() creates interesting problems with inline script. More often than not, any script that comes after it won't be able to use the results of the doc.write until after the page finishes loading -- or from within another document.write() block. Waiting for the window to be fully loaded could solve your issue. How about something like this -
Defined Once:
function callback(id) {
var el = document.getElementById(id);
if (el !== null) {
el.url = 'http://google.com';
}
}
Then anywhere you want to write out an iframe:
(function(){
var id = 'an_unique_Id';
document.write('<iframe id='+id+'></iframe>');
var l = window.onload;
var n = function() { someAsyncFunction(function() { callback(id); }); };
window.onload = (typeof l == 'function') ? function() { l(); n(); } : n;
})();
First, use:
document.getElementById(uniqid).url = 'http://google.com';
The method is part of the document object, so you can only use it by prepending document.
Second, use:
var callback = function() {
Will not break your code probably, but variables should be declared with var