jquery dynamically generated .append() sometimes appends to wrong div - javascript

I have a block of jquery that builds a form with more than 40 element using a loop to .append() div elements that load .ajax() json.
9 out of 10 times the form renders as expected. However randomly some of the elements will suffer from two types of errors
the dom element will be created but not displayed (does not show in source)(on deeper investigation, it seems the elements are always generated, just wrong parent (2))
the dom element is placed as a child to the wrong parent.
for example below, id3 should be attached to delta[14] but is instead generated at beta[3] OR id3 should be at gamma[6] but does not display at all
the format of build is
hard code
<div id="alpha">
<div id="beta"></div>
<div id="gamma"></div>
<div id="delta"></div>
</div>
doAppendStuff(beta, ajaxUrletc1);
doAppendStuff(beta, ajaxUrletc2);
...
doAppendStuff(gamma, ajaxUrletc10);
doAppendStuff(gamma, ajaxUrletc11);
...
doAppendStuff(delta, ajaxUrletc20);
doAppendStuff(delta, ajaxUrletc21);
dynamic
function randId(baseID) {
return baseID+"_"+Math.round(new Date().getTime() + (Math.random() * 555));
}
var id1= randId("myIdOne");
var id2= randId("myIdTwo");
var id3= randId("myIdThree");
function doAppendStuff(elemId, ajaxUrletc){
$('#' + elemId).append(
'<div id="' + id1 + '" >' +
'<div id="' + id2 + '">' +
'<select id="' + id3 + '"></select>' +
'</div>' +
'</div>'
);
... // log id1, id2,id3
... //do .ajax stuff + callback on id3
I have added callbacks to each loop to ensure that the .append is fired and no errors are generated complaining that the element does not exist.
Running a trace I can see the dynamic id for each element is being generated.
The code itself works as expected as the other 9/10 times it renders as expected.
notes
The random errors apply to different elements each time. no particular logic on what element fails.
I have separated the ajax calls from the dom element creation so there should be no bottleneck on the element creation
All ajax calls are initiated as expected in the correct order. Some take longer but the other elements generate as expected without waiting. All data is successfully returned.
Is there any know issues with generating multiple dom elements by calling the same function repeatedly OR is there a listener I could add to ensure the element is correctly generated in the desired position before proceeding to the next call via a callback.
UPDATE
After adding logging of the id1,id2,id3, the logs confirm that the correct dynamic ids are being assigned. It seems however that either the var in memory is being replaced with a previous value or the js engine is placing in the wrong generated position due to timing.
UPDATE 2
After some more debugging, we changed the random string and upped the number from
return baseID+"_"+Math.round(new Date().getTime() + (Math.random() * 555));
to
return baseID+"_"+Math.round(new Date().getTime() + (Math.random() * 99999));
and the problem has not reoccurred. So it looks like it could be either a random ID collision with the same string being generated twice or somehow reused when the function is reinitialised. The interesting thing is the ID's are not sequential, it will often skip several rows before reusing the same ID.
So we have cured the problem, but still do not understand what caused the issue, any thoughts are welcome.

If you want a unique value, then we're talking about GUID's.
Create GUID / UUID in JavaScript?
However, if you would have placed an underscore or other value between the time and random number, I'd suspect you'd have seen fewer collisions.
time + random = some future period in time.
Imagine getting a random 100 and then 100 ms later getting a random 0.
"abc1506110581013_100" != "abc1506110581113_0"
while
"abc1506110581113" == "abc1506110581113"

Related

Adding and Displaying Array Issue

var reset = function ()
{
var p = parseFloat($("#IA").val());
var q = parseFloat($("#IB").val());
var m = parseFloat($("#CGCD").val());
var aR = [];
aR += ["GCD(" + p + "," + q + ")=" + m];
document.getElementById("PGCD").innerHTML = aR + "\n";
document.getElementById("IA-error").innerHTML="";
document.getElementById("IB-error").innerHTML="";
$("#IA").focus();
};
The code above is only for a 'reset' function, a part of additional code (not present), the purpose which is to find the Greatest Common Denominator, GCD.
My 'reset' function is connected to a button, #reset, the purpose of which is to do four things:
add and store the string GCD(p,q)=m to the array 'aR'; p/q/m are variable stand-ins for the values of the input text areas #IA, #IB, and #CGCD (the GCD of #IA and #IB);
display the array 'aR' in a text-area #PGCD each time the reset button is clicked; this is why I used an array;
clear the two input text areas #IA and #IB;
clear the one output text area;
As it stands, all four objectives are completed successfully, with one exception: for the second objective, only the most recent GCD calculation is outputted; none of the previous calculations output.
I cannot get the array to list the different saved calculations within it. I think (?) the new calculations are being added to the array, but I am not sure.
I've tried a 'for' statement, and an 'if' statement, neither of which worked. I don't know whether I coded it wrong, or if it wasn't the right solution for my issue.
I tried to search the forums (here) for a solution, but was unable to find one.
Thank you.
If I'm understanding what you are describing, I believe your problem is that you are attempting to use += to add elements to an array. You should use
aR.push("GCD(" + p + "," + q + ")=" + m);
The += operator is used for addition of a value to itself as well as string concatenation.
Edit: per comments below, the main issue was declaration of aR as a local variable. It needs to be either global or declared within the same scope.

Loop Elements in JQuery

I am trying to run a loop over a few elements in JQuery. Before anyone says it, I do not need .each(). I am trying to run through the elements as a genuine loop- once a successful iteration runs, the loop will break and prevent the same action being done on other elements. I looked briefly at the straight JavaScript version, with the .getElement... methods, but it is my understanding that this won't satisfy my other requirement- the list of elements to be iterated over is created via a partial-string JQuery identifier:
rows = $('tr[id^="am_assetRow_' + parentAsset.replace(/ /, "_") + '_' + type + '"]');
Does anyone know of anything that might help me get this working?
EDIT: Just a bit more information on the application: I am checking to see if a value can be inserted into an existing row of a table, and if not, creating a new row and inserting it there. Thus, I need the loop to exit if a suitable fit is found, and after the loop terminates, I need to know whether it terminated in success (placing the value) or failure (no available locations- time to create a new row).
In jquery, if you want a $.each() loop to end immediately, just return false from the function call.
Do do a normal loop without using each() but still using jquery to select the items based on partial string etc...
rows = $('tr[id^="am_assetRow_' + parentAsset.replace(/ /, "_") + '_' + type + '"]');
for (var i = 0; i < rows.length; ++i) {
rows[i]; // The raw element at this index.
$(rows[i]); // jquery collection for this one element.
if (someCondition) {
break; // Break the loop early.
}
}

How does JQuery empty() method work?

I know that the empty method removes all children in the DOM element.
In this example however, why does removing the empty method result in duplicate entries:
and putting it in results in a normal page:
var renderNotesList = function()
{
var dummyNotesCount = 10, note, i;
var view = $(notesListSelector);
view.empty();
var ul = $("<ul id =\"notes-list\" data-role=\"listview\"></ul>").appendTo(view);
for (i=0; i<dummyNotesCount; i++)
{
$("<li>"+ "" + "<div>Note title " + i + "</div>" + "<div class=\"list-item-narrative\">Note Narrative " + i + "</div>" + "" + "</li>").appendTo(ul);
}
ul.listview();
};
I don't know why empty() doesn't work but I found this
... so until this is sorted everyone should just use:
el.children().remove(); instead of el.empty();
( jQuery.empty() does not destroy UI widgets, whereas jQuery.remove() does (using UI 1.8.4) )
Without seeing how your JavaScript is being used in your page, I suspect that you must be calling the renderNotesList() function twice and thus generating to unordered lists.
When you use the .empty() method, you are removing the first ul list, so you only see one instance. Without the call to .empty(), you retain both.
However, I can't say where or how this is happening in you web page without seeing more, but at least you now have some idea of what to look for.
Demo Fiddle
I built a demo using your JavaScript, but I was sort of guessing as to how you are using it.
Fiddle: http://jsfiddle.net/audetwebdesign/UVymE/
Footnote
It occurred to me that the function ul.listview() may actually be appending a second copy of the ul to the DOM. You need to check the code or post it for further review.

Form text field variable is showing undefined in Javascript

This is my first post, anyway I'll get straight to the point.
I have a form that when a user writes they're name in, Javascript saves the value to the global variable "ch_name". When I access this variable through an alert at any point on the site it always shows the correct value that I entered. However, it only works for alerts, when I try reference the user by using the variable it always shows as undefined.
I've tried re-ordering the code in case it hadn't loaded correctly and this hasn't yielded any results either. I've begun wondering whether I've referenced it right at all as I'm still fairly new to JavaScript.
As you'll probably find out my code is a mess but here's what I've got.
var ch_name; //my global variable
var data = new Array(); //I've taken a snippet out of an array of 8.
data[0] = '<p>Question 1</p>' + ch_name + "\n" + //ch_name is referenced on this line.
'<button onclick="results1()">Click Me</button>' + "\n" +
'<button onclick="results2()">Click Me</button>' + "\n" +
'<button onclick="results3()">Click Me</button>';
function charName(form) { //This is the code that changes the global variable ch_name.
ch_name = document.nameform.user.value;
ranData = Math.floor(Math.random() * data.length);
document.getElementById("placeholder").innerHTML = data[ranData];
}
The html snippet:
<form NAME="nameform" method="GET">
<p>Enter your character name:</p> <input type="text" name="user" value="" />
<input type="button" value="Start" onClick="charName(this.form)"/>
</form>
<div id="placeholder"></div>
Referencing the ch_name through an alert anywhere gives me the correct result, blank on page load and after the string is entered it gives the correct name. But anytime I reference it through the array variable it shows an undefined. This is not the full code but I'm certain there's no conflicts in variable names etc. I've scanned it a good few times, I'm certain it's just me being an idiot.
Is there a work around for this? I'm in a place ready to drop the whole code behind ch_name entirely to focus on other areas of the page.
Thanks for reading and any help.
I think that your problem is that when your data array is generated, ch_name doesn't have a value, so it comes out as blank. Even when you change ch_name, the data value won't be re-generated with the correct value.
A solution to this might be having a regenerate_data function that re-generates your data data array whenever you change the global character name.
The data won't change the way you seem to think.
I think for you the best bet would be to replace data Array with a function.
Now the ch_name parameter will be created dynamically every time you run the function. i.e. every time the button is pressed, which will update the data.
How do you change the data in a page?
var ch_name; //my global variable
function charName(form) { //This is the code that changes the global variable ch_name.
ch_name = document.nameform.user.value;
ranData = Math.floor(Math.random() * data.length);
document.getElementById("placeholder").innerHTML = getranData(ranData);
}
function getranData(num){
if(num==0)
return '<p>Question 1</p>' + ch_name + "\n" + //ch_name is referenced on this line.
'<button onclick="results1()">Click Me</button>' + "\n" +
'<button onclick="results2()">Click Me</button>' + "\n" +
'<button onclick="results3()">Click Me</button>';
else if (num==1) ; ///...and so on
}

JQuery - Fastest possible way of appending list items to list

I have some jquery that works fine, but I'd like to highly optimize it. Basically I'm
doing standard appending list items to unordered lists. Can anyone recommend the fastest
way to optimise the following code e.g. createDocumentFragment ?
for (key in data) {
li = $('<li><span class="item">' + data[key]["Name"] + '</span><img src=' + options.deleteIcon + ' alt="remove" class="delete"/></li>');
$('.item', li).data('ID', data[key]["Id"]);
$(list).append(li);
}
var sb = new Array();
for (key in data) {
sb.push('<li><span class="item" id="', data[key]['Id'], '">', data[key]["Name"], '</span><img src=', options.deleteIcon, ' alt="remove" class="delete"/></li>')}
$(list).append(sb.join(""));
I would suggest reducing the number of writes to the DOM to just one. By that, I mean storing the list into a temporary variable and then appending the entire list in a single operation. Also, instead of using .attr to set the ID of each element, you can use concatenation as you have used it to set the text of each LI.
var tmpList = '';
for (key in data) {
li = '<li><span class="item" id="' + data[key]['Id'] + '">' + data[key]["Name"] + '</span><img src=' + options.deleteIcon + ' alt="remove" class="delete"/></li>';
tmpList += li;
}
// if you are appending to an existing list, use append
// if you have just built one up from scratch, just use `.html`
$(list).append(tmpList);
I would recommend reading this:
Optimizing JavaScript For Execution Speed
From the article:
Unlike other programming languages,
JavaScript manipulates web pages
through a relatively sluggish API, the
DOM. Interacting with the DOM is
almost always more expensive than
straight computations. After choosing
the right algorithm and data structure
and refactoring, your next
consideration should be minimizing DOM
interaction and I/O operations.
For a start, you could make one large chunk of li elements and then append them in a single operation instead of appending them one at time.
Thanks everyone, the one point everyone seems to miss is that I'm associating the Id with the DOM element ... using the jquery.data method. This isn't to be confused the the ID attribute for the span element. Because of this I'm not sure how the concatenation would work as I understand that I need to have a reference to the DOM element to set the data on it. Is this correct?

Categories