Why would .append randomly fail in jquery? - javascript

I'm using jquery to add elements to a blank list.
on the page I have:
<ul id="myList">
</ul>
and I go through a loop like this in the script that's called from a dynamically created event handler. (It's "onDrop" of a list item having been sorted with a drag operation)
var myListItemHTML;
for (var i = 0 ; i < 5 ; i++)
{
myListItemHTML += '<li id=listItem'+i+'>This is item number'+i+'</li>';
}
$('#myList').append(myListItemHTML);
and if I check after...
if ($('#myList li').length == 0 )
{
alert('Going to crash now since I'm expecting list items')
}
roughly 95% of the time the list is populated, but about 5% of the time I hit my alert that's going to cause an exception later.
Has anyone run into this? Is there a callback or way to know when/if the append really happens?

var myListItemHTML;
for (var i = 0 ; i < 5 ; i++)
{
$('#myList').append('<li id=listItem'+i+'>This is item number'+i+'</li>');
}
Try just appending inside the for loop.
if ($('#myList li').length == 0 )
{
alert('Going to crash now since I\'m expecting list items')
}
You need a \ before the ' so it doesn't conflict.
edit: jsfiddle that shows "undefined" http://jsfiddle.net/gyEre/1/

This is a bit of a longshot, because I'm making an assumption about your code beyond what was posted. Here goes:
I had a problem with some code once, which worked perfectly in all Browsers except Chrome, wherein it would fail randomly and seemingly without cause.
My problem, ultimately, was that Chrome was actually executing my JavaScript too fast, and that it was throwing off some of the timing in some of the AJAX calls that were being made earlier.
My code was such that AJAX event A triggered, which then passed data to AJAX event B. In Chrome only, though, I found that event B was on occasion occurring before event A, and that was the error condition.
If I recall, I think the solution was to force one key AJAX request to be made synchronously, though that should be used with care. Please see: http://api.jquery.com/jQuery.ajaxSetup/
I hope that's not too vague to be helpful. Good luck!

What I've learned is that I can't trust jquery for DOM manipulation called through dynamically created events. I welcome someone to prove me wrong, but this approach:
addListItem = function (itemID, itemText)
{
var li = document.createElement('li');
li.setAttribute("id", itemID);
var liText = document.createTextNode(itemText);
li.appendChild(goalTextNode);
document.getElementById('myList').appendChild(li);
}
for (var i = 0 ; i < 5 ; i++)
{
addListItem('listItem'+i, 'Item Text'+i);
}
will work 100% of the time and never fail.

In my experience, if the script in which the prepend function is used in the -tag, it will not work because the script is loaded before the DOM is ready.
To avoid this kind frustration, is a good habit to place your javascript before the tag.

Related

Javascript function calling itself

I have a javascript function with two parameters : results which is an object array and i which is the index.
The function displays item from that array. I also want to to build links to show other entries in the array.
My code is:
function renderNews(results, i) {
$('.articleTitle').text(results[i].Title);
$('.articleBody').text(results[i].newsBody);
// Build links
var linkHtml = '';
for (var i = 0; i < results.length; i++) {
linkHtml += '' + (i + 1) + ' ';
}
$('.articleActions').html(linkHtml);
}
As you can see I am setting my onclick for the function to call itself to redraw the results. I get a "function not defined error".
I'm still very much learning as I go. Is it bad idea for a function to call itself? I wonder if anyone can advise on the right way of doing this.
If I understand, renderNews will be called when the page gets loaded, right? Actually, your links would be put inside a component with articleActions class. By your idea, clicking any link would call this function again, and all links would be replaced by a new links. This sounds strange. Also, I can't tell what do you expect when passing that results to the onclick event. Actually, if your idea was to always reuse the same results array, passing it undefinitely to the same function over and over again, you could make things much simpler:
function renderNews(results) {
if (results.length == 0)
return;
// Build links
var linkHtml = '';
for (var i = 0; i < results.length; i++)
linkHtml += '' + (i + 1) + ' ';
$('.articleActions').html(linkHtml);
$('.articleTitle').text(results[0].Title);
$('.articleBody').text(results[0].newsBody);
}
$('.article-link').click(function(){
$('.articleTitle').text($(this).data('articletitle'));
$('.articleBody').text($(this).data('articlebody'));
});
As far as I understand, whenever you want to update the current articles, you call renderNews which will build/rebuild a lot of links for each article in the array holding their data (title and body), and will load the first item. So renderNews is going to be called once the page loads (I don't know how you intend to do this).
There is a click event for any component with article-link class, in this case all your links (anchors). When one is clicked, it updates the screen (article's title and body) with its data.
You could improve the code to keep track of the selected item, and once renderNews is called, you load that article instead of the first one. Or you could keep passing the article's index as parameter, like your example.
Since I don't know how do you call renderNews function, it's hard to make a better code, but this might clear something to you.
Simple JSFiddle demo

Iterating over jQuery selector with variable, using closures

[First time on stackoverflow.] I am trying to dynamically add html buttons to my page and then give them a javascript function to run when they are clicked, using jQuery's click. I want to have one button for each element in an array, so I used a for loop. My code looks like this (simplified)
for (var i = 0; i < results.length; i++) {
$("#" + place[i].place_id).click(function(){console.log("Test");})
$("#" + place[i].place_id).click();
}
(I inject buttons with the right id's in the same loop.) This code, when run, console logs "Test" the right number of times, but afterwards, only the last button responds "Test" when clicked. (This situation is a little absurd.) So, I think the event handler ends up using only the final value of i to assign the event handler. I think the problem has to do with closures, but I am not sure how to make a closure out of a jQuery Selector (and in general am not familiar with them).
In contrast, as a hack solution, I "manually" wrote code like the below right below and outside the for loop, and it works as expected, in that clicking causes the console log.
$("#" + place[0].place_id).click(function(){console.log("Test"););
$("#" + place[1].place_id).click(function(){console.log("Test");});
etc.
(Of course, this all occurs within a larger context - specifically a Google Maps Places API call's callback.)
First, am I understanding the problem correctly? Second, what would work? Should I take a different approach altogether, like use a .each()?
(I later would want to display a property of place[i] when clicked, which I would think would need another callback
My final hack code looks like this:
$("#" + place[0].place_id).click(function(){google.maps.event.trigger(placeMarkers[0], "click"); repeated 20 times
To do this, you can simply create a self executing function inside the for loop, like this:
for (var i = 0; i < results.length; i++) {
(function(index) {
$("#" + place[index].place_id).click(function() {
//Do something with place[index] here
});
})(i);
}

Javascript taking too long to run

I have a script that is taking too long to run and that is causing me This error on ie : a script on this page is causing internet explorer to run slowly.
I have read other threads concerning this error and have learned that there is a way to by pass it by putting a time out after a certain number of iterations.
Can u help me apply a time out on the following function please ?
Basically each time i find a hidden imput of type submit or radio i want to remove and i have a lot of them . Please do not question why do i have a lots of hidden imputs. I did it bc i needed it just help me put a time out please so i wont have the JS error. Thank you
$('input:hidden').each(function(){
var name = $(this).attr('name');
if($("[name='"+name+"']").length >1){
if($(this).attr('type')!=='radio' && $(this).attr('type')!=='submit'){
$(this).remove();
}
}
});
One of the exemples i found : Bypassing IE's long-running script warning using setTimeout
You may want to add input to your jquery selector to filter out only input tags.
if($("input[name='"+name+"']").length >1){
Here's the same code optimised a bit without (yet) using setTimeout():
var $hidden = $('input:hidden'),
el;
for (var i = 0; i < $hidden.length; i++) {
el = $hidden[i];
if(el.type!=='radio' && el.type!=='submit'
&& $("[name='" + el.name + "']").length >1) {
$(el).remove();
}
}
Notice that now there is a maximum of three function calls per iteration, whereas the original code had up to ten function calls per iteration. There's no need for, say, $(this).attr('type') (two function calls) when you can just say this.type (no function calls).
Also, the .remove() only happens if three conditions are true, the two type tests and check for other elements of the same name. Do the type tests first, because they're quick, and only bother doing the slow check for other elements if the type part passes. (JS's && doesn't evaluate the right-hand operand if the left-hand one is falsy.)
Or with setTimeout():
var $hidden = $('input:hidden'),
i = 0,
el;
function doNext() {
if (i < $hidden.length) {
el = $hidden[i];
if(el.type!=='radio' && el.type!=='submit'
&& $("[name='" + el.name + "']").length >1) {
$(el).remove();
}
i++;
setTimeout(doNext, 0);
}
}
doNext();
You could improve either version by changing $("[name='" + el.name + "']") to specify a specific element type, e.g., if you are only doing inputs use $("input[name='" + el.name + "']"). Also you could limit by some container, e.g., if those inputs are all in a form or something.
It looks like the example you cited is exactly what you need. I think if you take your code and replace the while loop in the example (keep the if statement for checking the batch size), you're basically done. You just need the jQuery version of breaking out of a loop.
To risk stating the obvious; traversing through the DOM looking for matches to these CSS selectors is what's making your code slow. You can cut down the amount of work it's doing with a few simple tricks:
Are these fields inside a specific element? If so you can narrow the search by including that element in the selector.
e.g:
$('#container input:hidden').each(function(){
...
You can also narrow the number of fields that are checked for the name attribute
e.g:
if($("#container input[name='"+name+"']").length >1){
I'm also unclear why you're searching again with $("[name='"+name+"']").length >1once you've found the hidden element. You didn't explain that requirement. If you don't need that then you'll speed this up hugely by taking it out.
$('#container input:hidden').each(function(){
var name = $(this).attr('name');
if($(this).attr('type')!=='radio' && $(this).attr('type')!=='submit'){
$(this).remove();
}
});
If you do need it, and I'd be curious to know why, but the best approach might be to restructure the code so that it only checks the number of inputs for a given name once, and removes them all in one go.
Try this:
$("[type=hidden]").remove(); // at the place of each loop
It will take a short time to delete all hidden fields.
I hope it will help.
JSFiddle example

Saving Checkbox State In HTML or Javascript

I'm trying to design a site (possibly to make into a Chrome extension) that will allow me to mark which characters have done which events in an online game I play. I'd love it if I had the option to save the values of the checkboxes between visits (and ideally, character names as well), but I'm not great at coding. Also, I'd strongly prefer it if this information could be saved/cleared/reloaded on command, and not otherwise.
The page in question, please pardon me if my code isn't efficient or attractive. I actually prefer to work with it a bit more bunched up, but I tidied it for your benefits.
There had been a suggestion here that I was trying to work with:
var i, checkboxes = document.querySelectorAll('input[type=checkbox]');
function save() {
for (i = 0; i < checkboxes.length; i++) {
localStorage.setItem(checkboxes[i].value, checkboxes[i].checked);
}
}
function load_() {
for (i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = localStorage.getItem(checkboxes[i].value) === 'true' ? true:false;
}
}
But while it works fine in the test, it won't work when I put it into my document (you'll see the var i at the top still, the rest of the code I'd put above the reset function).
Help?
This line:
var i, checkboxes = document.querySelectorAll('input[type=checkbox]');
is in the head of your document and runs immediately. None of the checkboxes exist at the time that it runs.
I'd suggest putting that script at the end of your body element or placing it in a window.onload = function() {}.
If you inspect the DOM structure in the working jsFiddle link you posted, you'll see it places the script at the end of the body element.

JavaScript, loop all selects?

Well im new to javascript but why does this not work, all i want to do is to get a list of all selects on the page.
var elements = document.getElementsByTagName("select");
alert("there are " + elements.length + " select's");
for (i = 0; i < elements.length; i++)
{
alert(elements[i].getAttribute('Id'));
}
Edit: the error is that it does not find any selects at all, elements.length is allways zero!
You'r saying that elements.length is always returning 0 for you, this could be because:
You are running the JS code in the beginning of your page, thus the DOM is not fully available yet
Try using .id instead of of getAttribute('Id').
I guess the part of getting id attribute doesn't work for you. Probably it's because you typed there "Id" instead of "id".
The usual cause for getElementsByTagName returning zero results in a document with matching elements is that it is being run before the elements appear in the document (usually in the section and not inside a function that is called onload or onDomReady).
Move the element to just before the (END of body!) tag, or use an event handler that fires after the HTML has all been processed.
Well, as far as I can see is that perhaps the selects on your page don't have Id's (the alerts in the loop show null)

Categories