I'm trying to add <li>s to an existing <ul> with AJAX. It is a very simple test.
HTML:
<ul id="full_list">
<li>Existing item 1</li>
<li>Existing item 2</li>
<li>Existing item 3</li>
<li>Existing item 4</li>
<li>Existing item 5</li>
</ul>
<button type="button" onclick="addMoreItems()"></button>
JS:
function addMoreItems () {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
document.getElementById("full_list").appendChild = xmlhttp.responseText;
}
}
xmlhttp.open("GET", "filler.txt", true);
xmlhttp.send();
}
filler.txt:
<li>New item 6</li>
<li>New item 7</li>
<li>New item 8</li>
<li>New item 9</li>
<li>New item 10</li>
Desired result:
<ul id="full_list">
<li>Existing item 1</li>
<li>Existing item 2</li>
<li>Existing item 3</li>
<li>Existing item 4</li>
<li>Existing item 5</li>
<li>New item 6</li>
<li>New item 7</li>
<li>New item 8</li>
<li>New item 9</li>
<li>New item 10</li>
</ul>
<button type="button" onclick="addMoreItems()"></button>
I have no problems populating the <ul> with filler.txt's <li>s if I use innerHTML. I have spent time searching this and other sites, and have seen it said that unlike innerHTML which replaces all of an element node's contents, appendChild expects an object as opposed to a string, and so can't be added to the DOM after the page has loaded.
Unfortunately I'm still not able to figure out how to append a text string with a lot of <li>s back-to-back to the existing <ul>. Thoughts?
(The filler.txt document in xmlhttp.open()'s second parameter is something I'm only using for testing purposes. Once I figure out how to append more <li>s into the <ul> with AJAX, I'll use a PHP page instead which generates the <li>s with information from my MySQL db.)
This will give you the output:
var newItems = "
New item 6
New item 7
New item 8
New item 9
New item 10";
document.getElementById("full_list").innerHTML += newItems;
For simplicity, I used a variable already with html and added in innerHTML property.
If you really want to use appendChild(), rather than .innerHtml += ... try it like this:
var element = document.createElement("li");
var elementContent = document.createTextNode(xmlhttp.responseText);
element.appendChild(elementContent);
document.getElementById("full_list").appendChild(element);
Related
How do I iteratively add children elements to a (for example) a .
<ul id="my-list">
<li>item 1</li>
<li>item 2</li>
</ul>
If I have a JS script that runs something like this several times:
document.getElementById('my-list').appendChild('someListItemICreated')
the current 2 list items are removed. How do I add new li items to the ul without losing the current list itmes?
You need to provide an element as the argument for appendChild and not a string. Like this:
const li = document.createElement("li");
li.innerText = "Item 3";
document.getElementById("my-list").appendChild(li);
<ul id="my-list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
A much easier and a cleaner approach that I prefer in most of cases is:
document.getElementById("my-list").innerHTML += "<li>Item 3</li>";
<ul id="my-list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
const c = document.createElement('li');
c.innerText = 'item 3';
document.getElementById('my-list').appendChild(c);
<ul id="my-list">
<li>item 1</li>
<li>item 2</li>
</ul>
I think you need to be more specific with what your code is actually doing, but I can say that someListItemICreated should not be a string, if that's what you're passing. Here's an example I made that's similar to what you're referring to that runs fine.
<ul id="my-list">
<li>Item 1</li>
<li>Item 2</li>
</ul>
let someListItemICreated = document.createElement("li");
someListItemICreated.appendChild(document.createTextNode("item 3"));
document.getElementById("my-list").appendChild(someListItemICreated)
I wrote a code in PHP for parsing data that I received by an API request from "wikipedia.org".
I used DOMDocument class to parse the data and it worked perfectly fine. Now I want to do the same job in JavaScript. The API request returns (after a little cleaning up) a string like this:
$htmlString = "<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<ul>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>"
Note that this is just an example. Any request might have different number of lists, but it is always a series of unordered lists.
I needed to get the text inside the <li> tags and the following PHP code worked perfectly fine.
$DOM = new DOMDocument;
$DOM->loadHTML($htmlString);
$lis = $DOM->getElementsByTagName('li');
$items =[];
for ($i = 0; $i < $lis->length; $i++) $items[] = $lis[$i]->nodeValue;
And I get the array [Item 1,...,Item 5] inside $items variable as I wanted.
Now I want to do the same job in JavaScript. That is I have a string
htmlString = "<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<ul>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>"
in JavaScript and I want to get the text inside each of the <li> tags. I searched the web for an equivalent class to PHP DOMDocument in JavaScript, and surprisingly I found nothing.
Any ideas how to do this in (preferably Vanilla) JavaScript similar to the PHP code?
If not, any idea how to do this anyway in JavaScript (even maybe with regular expressions)?
Use DOMParser()
Your ported code, which is very much the same as your PHP:
let parser = new DOMParser()
let doc = parser.parseFromString(`<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<ul>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>`, "text/html")
let lis = doc.getElementsByTagName('li')
let items = []
for (let i = 0; i < lis.length; i++) items.push(lis[i].textContent)
console.log(items)
If you're working strictly with strings, you want to use Regular Expressions.
FYI
I'm using ES20xx syntax. If you can't support this, you'll need to convert to the syntax you're users can access.
Here I have an expressions that captures whatever is in between opening <ul> or <li> and the closing tags. Then I use the line breaks to split the string into an array. We need to filter out empty elements from the resulting array and finally return the desired items in a final array.
var htmlString = `<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<ul>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>`;
var lis = htmlString.replace(/<ul>|<li>(.*)<\/li>|<\/ul>/g, '$1').split('\n');
var items = lis
.filter(item => {
if (item && item !== null && item !== '') {
return item;
}
})
.map(item => {
var element = item.replace(/\s{2,}/g, '');
return element;
});
console.log('items array.', items);
is it possible, to add auto incremental classes to a list
<ul id="list">
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
<li>Element 4</li>
<li>Element 5</li>
</ul>
Now, If I hover on Element 3 then, add auto incremental classes to li like example below...
<ul id="list">
<li class="left2">Element 1</li>
<li class="left1">Element 2</li>
<li>Element 3</li>
<li class="right1">Element 4</li>
<li class="right2">Element 5</li>
</ul>
Again if hover on Element 1 then, add auto incremental classes to li like example below...
<ul id="list">
<li>Element 1</li>
<li class="right1">Element 2</li>
<li class="right2">Element 3</li>
<li class="right3">Element 4</li>
<li class="right4">Element 5</li>
</ul>
sorry about my poor English. Thank you.
$('li').hover(function() {
$('li').removeClass();
var next = $(this).nextAll();
next.each(function(i, v) {
$(this).addClass('right' + (i+1))
})
var prev = $(this).prevAll();
prev.each(function(i, v) {
$(this).addClass('left' + (i+1))
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="list">
<li>Element 1</li>
<li>Element 2</li>
<li>Element 3</li>
<li>Element 4</li>
<li>Element 5</li>
</ul>
Use .prevAll() and .nextAll()
Description: Get all preceding siblings of each element in the set of matched elements, optionally filtered by a selector.
Description: Get all following siblings of each element in the set of matched elements, optionally filtered by a selector.
then iterate on the li and assign the index. make sure to remove the li classes so it wont stack up
Based on example, I cleared all the class in <li> when mouseenter anyone of the <li> and add new class for them.
left1+n will add to previous all <li> and right1+n will add to next all <li>
$("#list > li").on("mouseenter", function(){
$("#list > li").attr("class", "");
$(this).prevAll("li").each(function(i) {
$(this).addClass('left' + (i+1));
});
$(this).nextAll("li").each(function(i) {
$(this).addClass('right' + (i+1));
});
});
I need to reorder some children elements with defined class and maintain the old order of the others that doesn't have the class. I have other sub elements
Code - html
<ul class="menu_dinamico">
<li class="reverseMenu">Item 1</li>
<li class="reverseMenu">Item 2</li>
<ul>
<li>item 2.1</li>
<li>item 2.2</li>
</ul>
<li class="reverseMenu">Item 3</li>
<li class="reverseMenu">Item 4</li>
<li>Item 4.5</li>
<li>Item 5</li> </ul>
Code - javascript
$.fn.reverse = [].reverse;
var list = $('.menu_dinamico');
var listItems = list.children('.reverseMenu');
list.prepend(listItems.get().reverse());
The result
item 4
item 3
item 2
item 1
item 2.1
item 2.2
item 4.5
item 5
The result wanted
item 4
item 3
item 2
item 2.1
item 2.2
item 1
item 4.5
item 5
P.s: I have to maintain the html structure like links, attributes etc.
Any clues? Thanks!
Edit:
After #bubicsaszar response I realized that my question was incomplete because my list can have infinite sub lists and their structure must be preserved.
try this, if the constant items always in the end of the list:
$.fn.reverse = [].reverse;
var list = $('.menu_dinamico');
var listItems = list.children('.reverseMenu');
list.prepend(listItems.get().reverse());
I use this code to delete the last item of an <ul> list, but only on the second, fourth, sixth, ... every second click on the button the item gets removed, but every click the message appears. What can I do that the element gets deleted on every click.
document.getElementsByTagName('input')[0].onclick = function () {
'use strict';
var list = document.getElementById('list'), item = list.lastChild;
list.removeChild(item);
window.alert("Removed");
};
<ul id="list">
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
<li id="child">List item 4</li>
<li>List item 5</li>
</ul>
<input type="button" value="Delete last">
This is because .lastChild returns all nodes, including empty text nodes which exist in your <ul>. Use .lastElementChild instead to target your <li> nodes
The difference between this property and lastElementChild, is that
lastChild returns the last child node as an element node, a text node
or a comment node (depending on which one's last), while
lastElementChild returns the last child node as an element node
(ignores text and comment nodes).
See HTML DOM lastChild Property and HTML DOM lastElementChild Property for more information
document.getElementsByTagName('input')[0].onclick = function () {
var list = document.getElementById('list'), item = list.lastElementChild;
list.removeChild(item);
};
<ul id="list">
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
<li id="child">List item 4</li>
<li>List item 5</li>
</ul>
<input type="button" value="Delete last">
Some more details on why this is happening... When you format your markup with clean spacing, what I think of as "phantom" text nodes silently exist within your <ul>. If you minify this markup to the following, your first example would indeed work fine
<ul id="list"><li>List item 1</li><li>List item 2</li><li>List item 3</li><li id="child">List item 4</li><li>List item 5</li></ul>
Plunker Link - minifed markup example using .lastChild
Change:
item = list.lastChild;
to
item = list.lastElementChild;