function initnav(){
var cbox=document.getElementsByClassName('box');
for(var i=0;i<cbox.length;i++){
cbox[i].innerHTML=cbox[i].id;
<!--so far I have set their captions after setting their ids same as how I want them to appear.-->
<!--what comes next is what doesn't really work.-->
getElementById(cbox[i].id).onclick=Mclick(cbox[i].id);
}
};
function Mclick(id){alert(id);}
The whole thing is in a js file, and promptly linked from my html file.
As planned, all the buttons should appear and be clickable, but what is happening instead is only one of them is visible and that one is not working when I click on it.
When I create a lot of div-oriented buttons, I wish I could run for loop and be able to assign each of them as clickable instead of writing lines as many as they are.
How do you assign onclick within for loop in javascript?
You're calling the function instead of assigning it.
getElementById(cbox[i].id).onclick = Mclick;
Of course, now your function will receive an event argument instead of an id. (Passing the id inside the loop is a huge pain; easiest fix is to not bother trying.) But it also gets the attached element as this, which is convenient:
function Mclick() {
alert(this.id);
}
Other comments:
You should try not to be in the habit of using innerHTML if you're not assigning a string that contains known HTML. Saves you from having to care about escaping. Use textContent instead.
Assigning to onclick is a bit inflexible; you can only ever assign one click handler this way, and it's hard to notice if you accidentally overwrote an existing handler. Use addEventListener.
getElementById(element.id) should surely be equivalent to element.
Don't use HTML comments within JavaScript! :) They only work for... weird backwards-compatibility reasons. JavaScript comments are either // ... or /* ... */.
Best not to capitalize a function name unless it's supposed to be a constructor; you may notice that SO's highlighting made Mclick green, because it thinks it's a class name.
So I'd end up with:
function initnav() {
var cbox = document.getElementsByClassName('box');
for(var i = 0; i < cbox.length; i++) {
cbox[i].textContent = cbox[i].id;
cbox[i].addEventListener('click', alert_id);
}
}
function alert_id(event) {
alert(this.id);
}
So basically you don't call the for loop since the for loop is in the function. If you want to call all your variables and the statements in the for loop you have put the statements in the function and call the function outside of the function but inside of the script.
Related
jQuery currently uses window as its default element so any call like $('div') will look for div tags inside window.
Is there any way to change defaults on jQuery like:
$.defaultRoot = $('.anyOtherRootElement');
$('div').text("Hello");
this will select any div inside the elements containing .anyOtherRootElement class.
Thanks in advance
Upate
just an update refining the question a bit more here:
I would like to perform the actions above based on external queries coming from external script which won't know what defaultRoot is so they can still be calling what is supposed to be the current base, so in this instance, I'm afraid adding the a second parameter wouldn't be an option, unfortunately.
And at the same time creating a function which returns defaultRoot.find(el) would prevent me of using first-level methods such $.trim, $.each, etc… so unfortunately that would not be possible as well.
Ideally (for performance reasons) you'd want to use find()
$.defaultRoot.find("div");
Otherwise you can use the 2 argument form that sets a context
$("div", $.defaultRoot);
In general you don't want to do these types of things implicitly since someone else could easily end up thoroughly confused when having to work with your code later. If you want to do it consistently and make it shorter you should create your own function to do so like:
var $s = function(selector) {
return $.defaultRoot.find(selector);
}
and then you'd just be able to use
$s("div")
or you could also do a scoped higher order function with something like
var withScope = function(scope$) {
return function(selector) {
return scope$.find(selector);
}
}
var $s = withScope($.defaultRoot);
$s("div")
If for some reason you really want to screw around with the default state for client code (begging for chaos IMO), you should look at the functional practice: currying.
$('SELECTOR', 'CONTEXT')
You can use context. As in your case $('div', '.anyOtherRootElement')
For more details, visit http://api.jquery.com/jQuery/
Given that you can pass the context as a second argument, you can easily overwrite the $() operator in Javascript with a version which internally calls JQuery using jQuery.noConflict(); and always passes your new root as the second argument.
I don't think jQuery provide such method or variable. But you can pass second parameter in jQuery method to set context.
$.defaultRoot = $('.anyOtherRootElement');
$('div', $.defaultRoot ).text("Hello"); // all div inside $('.anyOtherRootElement')
$('div' ).text("Hello"); //all div inside body tag
I have a number of divs I am toggling on and off. I initially was manually binding the handlers(as per the code below), but decided to do some refactoring. However, a binding issue arose where the last key/value in the hash is the one always selected. In the example code, that would be the contact_data div. I think the problem is that the data is not being closed over, but I am not certain how to force this in JS.
var link_div = {
"#about_toggle" : "#about_stuff",
//more pairs
"#contact_toggle" : "#contact_data"
};
/*
* Before refactoring:
$("#about_toggle").click( function()
{
$("#about_stuff").toggle();
});
*/
//After
for(var key in link_div)
{
$(key).click(function()
{
alert(link_div[key]);
toggle_on_element(link_div[key]);
});
}
Actually the problem is, the data is being closed over. The anonymous function you pass in the event handler will close over your loop variable, therefore all event handlers reference the same variable. You need to invoke another function to avoid this:
for(var key in link_div)
{
$(key).click(function(k)
{
return function() {
alert(link_div[k]);
toggle_on_element(link_div[key]);
}
}(key));
}
It's all about closures, which go back to the environment frame binding model of the language. Essentially key iterates through your loop and at the end, points to the last value in the map (which by the way, may not guarantee order, im not sure of the specific implementations). As such, the anonymous function (which is shared among all the elements, because it was created once, and therefore refers to one environment frame in memory) will for all elements, toggle link_div[key], but key, for all elements, has only one value.
You can solve this by either wrapping the anonymous function to make it unique for each binding (like jAndy did), or use a naming convention to make life a little easier:
$('.togglers').click(function(){ $('#'+$(this).attr('id')+'_stuff').toggle(); });
Say i have a selection of textboxes like this;
var arrayoftextboxes = $('.textbox1, .textbox2, .textbox3');
Is there a way I can call a function on each one in a simpler way than this?
It only needs to be called once.
arrayoftextboxes.each(function(i){foo(arrayoftextboxes[i]);});
I tried
arrayoftextboxes.load(function(){foo(this)});
and
arrayoftextboxes.bind(function(){foo(this)});
but the functions dont seem to be called.
You can do this:
$('.textbox1, .textbox2, .textbox3').each(function() { foo(this); });
The .each() call creates a closure, inside it this refers to the DOM element you're currently on, but it may be better to write what you have as a jQuery plugin. Or, if you just use this inside foo (instead of the DOM element as a parameter) you can shorten it down to:
$('.textbox1, .textbox2, .textbox3').each(foo);
Here's a demonstration of that method
Also, make sure you're running this on document.ready like this:
$(function() {
$('.textbox1, .textbox2, .textbox3').each(foo);
});
Otherwise the DOM elements may not be there to find, making that selector return an empty array (so nothing to run on).
Yes, I am having issues with this very basic (or so it seems) thing. I am pretty new to JS and still trying to get my head around it, but I am pretty familiar with PHP and have never experienced anything like this. I just can not empty this damn array, and stuff keeps getting added to the end every time i run this.
I have no idea why, and i am starting to think that it is somehow related to the way chekbox id's are named, but i may be mistaking....
id="alias[1321-213]",
id="alias[1128-397]",
id="alias[77-5467]" and so on.
I have tried sticking
checkboxes = []; and
checkboxes.length = 0;
In every place possible. Right after the beginning of the function, at the end of the function, even outside, right before the function, but it does not help, and the only way to empty this array is to reload the page. Please tell me what I am doing wrong, or at least point me to a place where i can RTFM. I am completely out of ideas here.
function() {
var checkboxes = new Array();
checkboxes = $(':input[name="checkbox"]');
$.each(checkboxes,
function(key, value) {
console.log(value.id);
alert(value.id);
}
);
checkboxes.length = 0;
}
I have also read Mastering Javascript Arrays 3 times to make sure I am not doing something wrong, but still can't figure it out....
I think there's a lot of confusion coming out of this because you are clearing the array -- just maybe not for the purpose you want, or at the wrong time, etc.
function () {
var checkboxes = new Array(); // local-scope variable
checkboxes = $(':input[name="checkbox"]'); // new instance
$.each(checkboxes, /* ... */); // (truncated for brevity)
checkboxes.length = 0; // this clears the array
// but, for what...?
// nothing happens now
}
As per your snippet, every call to the function recreates and clears the array. However, all work with the array is done while the array is full (nothing happens after it's cleared).
Also, checkboxes is a private variable to the function. The variable only exists during execution of the function and is forgotten once the function is done.
So, what is the big picture? Why are you trying to clear the array?
To take a guess, it sounds like you're intending on clearing it for the next call of the function.
i.e. (filling in doSomething for function name):
doSomething(); // log all array elements and clears the array
doSomething(); // does nothing, since the array is already empty
To accomplish this, you need to define checkboxes in a single location outside of the function, either as a global variable or using closures (the heavily more recommended, albeit more complex, option):
NOTE: If you haven't dealt with closures before, they may not make much sense after only a single example. But, there are thousands of resources available, including Stack Overflow, to help explain them better than I can here.
// closure (or instantly-called function)
// allows for defining private variables, since they are only known by
// code blocks within this function
(function () {
// private (closure-scoped) variable(s)
var checkboxes = $(':input[name="checkbox"]');
function doSomething() {
$.each(checkboxes, /* ... */);
checkboxes.length = 0;
}
})();
The closure will run once, defining checkboxes as the array of inputs while doSomething will iterate the array before clearing it.
Now, the last step is to expose doSomething -- cause, as with checkboxes, it is also private. You can accomplish exposing it by passing the function reference from the closure to a variable outside the closure:
var doSomething = (function () {
/* ... */
return doSomething; // note that you DO NOT want parenthesis here
})();
Is setting length even possible? ;)
Any way checkboxes is a jquery object not an array.
When You do checkboxes = $(':input[name="checkbox"]'); it's not an array any more and whatever there was before has no influence. It doesn't matter what was in a variable if You assign something new to it in any language I know.
You are making some jquery related error. Please elaborate more so that I can help
Are You sure You put name="checkbox" in all of them? It doesn't seem to have a lot of sense. Maybe You waned $(':input[type="checkbox"]'); ?
Edit: that's funny. the above selector isn't too good as well. It should be:
$('input:checkbox');
as for removing stuff:
delete varname
delete arrname[elem]
is the right way to do it.
assigning null does not change the length but just makes trouble.
What about doing this:
checkboxes = new Array();
You can also delete it.
delete checkboxes;
When you rerun the function it will search for all checkboxes again.
Consider this strategy:
function() {
var checkboxes = $(':input[name=checkbox]:not(.examined)');
checkboxes.addClass('examined');
$.each(checkboxes, function(key, value) {
// ... stuff
});
}
You could also use .data() if you don't want to "pollute" the DOM.
I have an HTML document (here), which creates an iframe-based media player for a collection of songs within albums (I just used letters to define these albums and songs in the mymusic array, for simplicity).
Focusing on the top 3 iframes, the way I have set out the user interaction is to generate the HTML for forms of available albums and songs using Javascript, and write them to the iframes in the body. If you run it and make a selection in the Albums menu, you will see that the options in the Songs menu correspond with the mymusic array, so this works.
However, when I choose a song, the function nowplaying(trackindex,albumindex) should be called using an onchange event in the Songs form, the same way as in the form generated using showinitial() ... but the function does not get called.
I have ruled out the coding of nowplaying itself as a cause, because even when I change nowplaying to alert("hello"), it does not get called. So this leads me to think the problem is with the onchange attribute in "anything", but I can't see the problem. The way I coded it is no different to before, and that worked fine, so why won't this work?
Any help would be much appreciated!
Firebug is your friend....
i is not defined
function
onchange(event) {
parent.nowplaying(this.SelectedIndex,
i); }(change )
onchange is getting called, but i is not defined when calling nowplaying.
This is the result of this line:
p+="<html><head></head><body><form><select onchange='parent.nowplaying(this.SelectedIndex,i);' size='";
which is using "i" in the string, when it should append it as a variable:
p+="<html><head></head><body><form><select onchange='parent.nowplaying(this.SelectedIndex," + i + ");' size='";
To clarify, i is defined when anything(i) is called, but you aren't writing i into the code, just the letter "i". When nowplaying(this.SelectedIndex,i) is called, i is no longer defined, because you aren't inside of the anything() function anymore. You need to expand i when you append the html to p, so that the value is there and not the variable i.
function anything(i){
p+="...<select onchange='parent.nowplaying(this.SelectedIndex,i);'...";
Your onchange event handler is set from a string. When run, it will not have access to i, which is a local variable from the anything function that has long since gone away.
The simple fix would be:
p+="...<select onchange='parent.nowplaying(this.SelectedIndex,'+i+');'...";
which turns the current value of i at string-making time into an integer literal inside the string.
However, it's not generally a good idea to be creating code from strings. It's normally better to write the event handler as a normal function object:
// You will need the below workaround to get the iframe document in IE too
//
var iframe= document.getElementById('songs');
var idoc= 'contentDocument' in iframe? iframe.contentDocument : iframe.contentWindow.document;
idoc.open();
idoc.write(s);
idoc.close();
idoc.getElementsByTagName('select')[0].onchange= function() {
// This is a closure. The 'i' variable from the parent 'anything' function is
// still visible in here
//
parent.nowplaying(this.selectedIndex, i);
};
However you would generally want to avoid setting handlers from one frame on a different one. I'm not really sure what the iframes are gaining you here other than headaches. Why not just simply use positioned divs with overflow? You can still rewrite their content through innerHTML if you need to... though I would prefer to populate them using DOM methods, to avoid all the HTML-injection problems your current script has.