I've got a script that adds li elements to a ul and assigns some attributes to it based on a user's selection in a select element. However, I figured it would be necessary to make sure that the li element doesn't already exist in the ul. However, I'm not sure, but I think that either the array isn't assigning variables to it or there's something wrong with my if statement that compares the array. Of course, I may be totally off. I'm stumped.
function anotherCounty(){
var newCounty = document.forms['newForm'].county.value;
var ul = document.getElementById("counties");
var items = ul.getElementsByTagName("li");
var itemArray = new Array();
for (var i = 0; i < items.length; i++){
itemArray.push(items[i]);
}
if (itemArray.indexOf(newCounty)<'0'){//I've also tried ==-1 and <0
var new_item = document.createElement("li");
new_item.id = "addCounty[]";
new_item.innerHTML = newCounty;
ul.insertBefore(new_item, ul.firstChild);
}
}
You are comparing a string with DOM elements. I think this is what you actually need:
itemArray.push(items[i].value);
Instead of using indexOf, you'd be a lot better off to use this approach:
function anotherCounty() {
var newCounty = document.forms['newForm'].county.value;
var ul = document.getElementById("counties");
var items = ul.getElementsByTagName("li");
for (var i = 0; i < items.length; i++){
if(items[i].value == newCounty) return;
}
var new_item = document.createElement("li");
new_item.id = "addCounty[]";
new_item.innerHTML = newCounty;
ul.insertBefore(new_item, ul.firstChild);
}
I know this thread is old, but I just had the same issue and what worked for me was
itemArray.push(items[i].innerHTML);
Related
This is my code
var menuLi=[];
$(window).load(function() {
var menuUl = document.getElementById("sub");
menuLi = menuUl.getElementsByTagName("LI");
var len = menuUl.childNodes.length;
nSpan = document.createElement("SPAN");
var findUl;
for (var i = 0; i < len; i++) {
findUl = menuLi[i].getElementsByTagName("UL");
liLen = findUl.length;
if (liLen > 0) {
menuLi[i].classList.add("myNew");
}
}
});
The above is my code which throws error menuLi[i] is undefined.
Problem:
In fact you are looping using the wrong length.
Explanation:
Because the menuLi.length is lower than menuUl.childNodes.length, as menuUl.childNodes will contain all the li inside menuUl and their children.
So menuUl.childNodes.length is the number of li elements multiplied by the number of elements in each li.
So i will get greater than the menuLi.length, and that explains why you got menuLi[i] is undefined, because if you have only 5 li in menuUl and for example you are trying to get menuLi[8], where menuLi[8] is undefined.
Solution:
So just use:
var len = menuLi.length;
You are looping from 1 to menuUl.childNodes.length using i, but you are indexing straight into menuUl[i]. Do you mean menuUl.childNodes[i]?
here is the deal, i have the following jquery code that should add the array values to specific #id, buf it does not replace the code, only add more, and i need a little help to make it replace the html on othe link click.
Code:
function changeClass(curClass){
switch(curClass){
case "Schoolgirl":
case "Fighter":
var charSkillsNames = ["text1","text2","text4","text5"];
//loop show array values
listSkillsNames(charSkillsNames);
break;
}
}
function listSkillsNames(arr){
var length = arr.length,
element = null;
$("#skills").html(function(){
for (var i = 0; i < length; i++) {
element = arr[i];
$(this).append("<li>"+element+"</li>");
}
});
}
this works well but i need it to replace the html inside the "#skills" id when i click on the link that makes it work
PS: problem is really here
The issue is that you don't empty the HTML of #skills element. Use $("#skills").html("") to empty it.
function listSkillsNames(arr){
var length = arr.length,
element = null;
var $skills = $("#skills");
$skills.html(""); // empty the HTML
for (var i = 0; i < length; i++) {
element = arr[i];
$skills.append("<li>"+element+"</li>"); // append new items
}
}
The problem is because you are keep appending new items to the element always without removing the existing items.
Just empty the skill element, also there is no need to use the .html(function(){}) here
function listSkillsNames(arr) {
var length = arr.length,
element = null;
var $skill = $("#skills").empty();
for (var i = 0; i < length; i++) {
element = arr[i];
$skill.append("<li>" + element + "</li>");
}
}
Here's my code:
function listDesserts (){
var dessertList = ["pudding", "cake", "toffee", "ice cream", "fudge", "nutella"];
var i = 0;
while (i< dessertList.length){
var ul = document.getElementById("thelist");
var nli = document.createElement("li");
var nliID = 'item-' +i;
nli.setAttribute('id', nliID);
nli.setAttribute('class', 'listitem');
nli.innerHTML = dessertList[i];
ul.appendChild(nli);
i++;
}
}
Since I'm setting the li tags IDs based on the number items in my array, i sets it to zero as it should. Rather I want to modify i so that it sets the IDs beginning with 1 without it skipping the first array member. I've tried a few things but I'm missing this. Anybody?
As you are iterating an array, the counter variable should always run from 0 to length-1. Other solutions were possible, but are counter-intuitive.
If you have some one-based numberings in that array, just use i+1 where you need it; in your case 'item-'+(i+1).
Btw, you might just use a for-loop instead of while.
Use var i = 1;
and use i-1 where you currently have i
var i = 1;
while (i< dessertList.length-1){
var ul = document.getElementById("thelist");
var nli = document.createElement("li");
var nliID = 'item-' + (i-1); //<----- here
nli.setAttribute('id', nliID);
nli.setAttribute('class', 'listitem');
nli.innerHTML = dessertList[i-1]; //<----- and here
ul.appendChild(nli);
i++;
}
As you can see I am still a novice in javascript
Why is it so that you can append a Textnode only once? When you add it again somewhere else the first one disappears
I do not need a solution to a problem I was just curious what is causing this behavior.
Example where the textnode is only added to the last element of an array:
function hideAdd(){
var hide = document.createTextNode('Afbeelding verbergen');
var afb = collectionToArray(document.getElementsByTagName('img'));
afb.pop();
var divs = [];
for (i=0; i < afb.length; i++){
divs.push(afb[i].parentNode);
}
console.log(divs);
for ( i = 0; i < divs.length;i++){
divs[i].appendChild(hide);
}
}
This is where you use an unique textnode so it works:
function hideAdd(){
var hide = []
var afb = collectionToArray(document.getElementsByTagName('img'));
afb.pop();
var divs = [];
for (i=0; i < afb.length; i++){
divs.push(afb[i].parentNode);
hide[i] = document.createTextNode('Afbeelding verbergen');
}
console.log(divs);
for ( i = 0; i < divs.length;i++){
divs[i].appendChild(hide[i]);
}
}
Short answer is the DOM is a tree, not a network. Each node can have only one parent. If you could add a node in more than one location, it would have more than one parent.
I was asked a question in yestoday's interview:
How do you reverse a <li> list
efficiently?
for example, if there is a list:
<ul id="list">
<li>1</li>
<li>2</li>
...
<li>10000</li>
</ul>
then after the reverse, the list would look like:
<ul id="list">
<li>10000</li>
...
<li>2</li>
<li>1</li>
</ul>
The most efficient code I can come up with is this:
function reverse(){
var list = document.getElementById("list");
var node_list = list.childNodes;
var fragment = document.createDocumentFragment();
for(var i=node_list.length-1; i>=0; i--){
fragment.appendChild(node_list[i]);
}
list.appendChild(fragment);
}
But it's still really slow(takes about 10s in Chrome). Any idea?
UPDATE:
I think there is something wrong with my Chrome... I installed a Chromium and tested the code above in it, it takes less a second.
I guess the point of the interview question basically is the fact that innerHTML is way faster than any DOM operation in every browser. So, don't use DocumentFragment, use a simple string instead:
var ul = document.getElementById("list");
var lstLi = ul.childNodes;
var str = '';
for (var i=lstLi.length - 1; i >= 0; i--) {
str += '<li>' + lstLi[i].innerHTML + '</li>';
}
ul.innerHTML = str;
http://jsfiddle.net/bKeuD/
The way the DOM work, you don't need to re-create the zone. All you need to do is move your element inside the ul that already exist. An optimal solution would be something along this :
var ul = document.getElementById("lstLi");
var lstLi = ul.childNodes;
for (var i=0, c = lstLi.length; i < c; i++) {
ul.insertBefore(lstLi[i], ul.firstChild);
}
Basicly what this does is that it iterate over each element an put them in first. In the end your list will be reversed.
You can do it this way:
var list = document.getElementsByTagName("ul")[0],
items = list.childNodes,
itemsLen = items.length;
while (itemsLen--) {
list.appendChild(items[itemsLen]);
}
Test: http://jsbin.com/ohegu4/2/edit
And if your problem is that you don’t want to block the browser, you can do that:
var list = document.getElementsByTagName("ul")[0],
items = list.childNodes,
itemsLen = items.length;
(function reversePart() {
var iterations = 10; // Number of processed elements every 100ms
window.setTimeout(function(){
while (iterations-- && itemsLen--) {
list.appendChild(items[itemsLen]);
}
if (itemsLen) {
reversePart();
}
}, 100); // Delay between each process : 100ms
})();
Test (with 100000 li, yeah!): http://jsbin.com/ubugi3/2/edit
Pumbaa80's string-based method is the fastest way I found to reverse the list. But just in case you really want to reverse it using DOM methods (e.g. you don't want to lose the attributes of the list items), you can do it this way:
function reverseDom(){
var list = document.getElementById('list');
var items = list.childNodes;
var length = items.length;
var item0 = items[0];
var i;
list.style.display = 'none';
for(i = 0; i < length - 1; i++)
{
list.insertBefore(list.lastChild, item0);
}
list.style.display = 'block';
}
In Google Chrome in Linux, the method above took about a second to reverse a 10,000 item list. But Pumbaa80's method is faster. You can compare the two methods side by side on the same list if you go to this link:
http://jsbin.com/ubugi3/6