Is this function going to be able to insert li elements in the dashed spaces in the following html code?
const shoppingList = document.getElementById("shoppinglist");
var li = document.createElement('li');
for (let i = 0; i < shoppingList.children.length; i++) {
shoppingList.insertBefore(li, shoppingList.children[i]);
if (i == shoppingList.children.length - 1) {
shoppingList.appendChild(li);
}
}
<ul id="shoppinglist" class="collection">
----
<li class="collection-item" id="listitem:Where" draggable=true></li>
----
<li class="collection-item" id="listitem:There" draggable=true></li>
----
</ul>
No, it won't.
You create only one list element and then are trying to insert it multiple times. You have to create a new element in each iteration.
The shoppingList.children is constantly updated within the for loop, therefore the condition inside for(...) declaration is not going to work as you'd expect.
The following code should work. The difference is, that I take references to only existing list items and try to prepend new list items relative to them. Finally I just append a new list item to the whole tree, therefore I fulfill even the condition when none children are present.
const shoppingList = document.getElementById('shoppinglist')
let i = 0
const createLi = () => {
const li = document.createElement('li')
li.innerText = `New ${++i}`
return li
}
Array.from(shoppingList.children)
.forEach(c => shoppingList.insertBefore(createLi(), c))
shoppingList.appendChild(createLi())
<ul id="shoppinglist">
<li>Original 1</li>
<li>Original 2</li>
</ul>
Related
Just staring to lean js here. Is it possible to move child elements from one parent under another parent if parent name is duplicate?
Example:
How to put Something else & Something like under a single Composition parent, and Someone else under one Content, and delete the duplicates.
Is this possible with javascript or jquery? Thank you!
<li>
<h4>Composition</h4>
<p>Something</p>
</li>
<li>
<h4>Composition</h4>
<p>Something else</p>
</li>
<li>
<h4>Composition</h4>
<p>Something like</p>
</li>
<li>
<h4>Content</h4>
<p>Someone</p>
</li>
<h4>Content</h4>
<p>Someone else</p>
</li>
here is the js I got so far, I'm stuck at moving p items to the first element that repeats, each under it's parent. thing is parent elements don't have certain id's or classes:
const $technicalProp = $("[data-surface-prop]");
const technicalVal = $("[data-surface-val]");
let duplicate = {};
$technicalProp.each(function() {
let txt = $(this).text();
if (duplicate[txt]) {
$(this).remove(); // this only removes the dulicate names I also need to move chlidren
}
else {
duplicate[txt] = true;
}
});
yes you can i dont know if this is the best way but this works, my approach is by making an arrayobject (js) as a new model of what you want
the logic is i extract the text of h4 and check if the data variable has been registered there, if not registered yet then i push it to the data and the child text (p)
if registered already then i push the child text only
let data = [];
const lists = document.querySelectorAll('li').forEach( (li, liIdx) => {
let parentName = li.querySelector('h4').innerHTML;
let childName = li.querySelector('p').innerHTML;
let isRegistered = data.find( x => x.parentName === parentName);
if(!isRegistered) {
childName = [childName];
data.push({ parentName, childName});
}else {
console.log(isRegistered)
isRegistered.childName.push(childName);
}
});
and this is what we got
0:
childName: (3) ["Something", "Something else", "Something like"]
parentName: "Composition"
1:
childName: (2) ["Someone", "Someone else"]
parentName: "Content"
then reset the ol tag and rebuild its item
let ol = document.querySelector('ol');
ol.innerHTML = '';
data.map( d => {
let p = d.childName.map( c => `<p>${c}</p>`).join('');
ol.insertAdjacentHTML('beforeend', `
<li>
<h4>${d.parentName}</h4>
${p}
</li>
`);
})
With jQuery you can remove and add elements with detach(), append() and prepend()
Like here: https://stackoverflow.com/a/19802593/11265780 (but replace jQuery with $)
I'm looking for a way to add a class to a certain element with another class.
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li class="hide">Item 4</li>
<li class="hide">Item 5</li>
<ul>
JS/Jquery
if($('li').hasClass('hide')) {
$('li').removeClass('hide').addClass('slide-down');
}
The problem is that the class slide-down gets added to all li elements.
Is there a way to only target the li elements that have the hide class removed?
Mh maybe it's due to the typo in your HTML: class"hide" (you are missing the equal sign).
Also you got a logical error in your code:
if($('li').hasClass('hide')) the condition will yield true if any <li> element in your document has the hide class.
$('li').removeClass('hide').addClass('slide-down'); the first segment $('li') will actually select ALL <li> elements in your document and remove the class hide from them and add the slide-down to ALL <li> elements in your document.
Here's how I'd do it:
$('li.hide').removeClass('hide').addClass('slide-down');
Note that jQuery is about chaining, i.e selecting subsets and applying functions to these subsets.
What this line does is:
$('li.hide') selects all <li> elements in your document which have the hide class - this becomse your "working subset" now.
.removeClass('hide') removes the hide class from this subset we got in the first step and returns the same subset again.
.addClass('slide-down') adds the slide-down class to all <li> in the selected subset returned from step 2, which is the same as from step 1.
JS fiddle: https://jsfiddle.net/q0nzaa7t/
In vanilla JS:
var liHide = document.querySelectorAll('li.hide');
var i;
var length = liHide.length;
for (i=0;i<length;i++) {
liHide[i].className = 'slide-down';
}
Note that, for some reason, querySelectorAll doesn't get updated automatically like document.getElementsByClassName. The same code wouldn't work if we would have used that method for querying the DOM:
var liHide = document.getElementsByClassName('hide');
var i;
var length = liHide.length;
for (i=0;i<length;i++) {
liHide[i].className = 'slide-down'; //<-- this won't update the 2nd element
}
This would have only changed the first element, since liHide[1] becomes liHide[0], because <li class="hide">Item 4</li> is no longer part of HTML Collection.
Plain javascript for the ones with querySelectorAll and classList support:
var items = document.querySelectorAll('li.hide');
for (var i = 0; i < items.length; i++) {
items[i].classList.remove('hide');
items[i].classList.add('slide-down');
}
Without querySelectorAll:
var items = document.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
if (items[i].classList.contains('hide')) {
items[i].classList.remove('hide');
items[i].classList.add('slide-down');
}
}
Without querySelectorAll and classList:
var items = document.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
if (new RegExp(/(?:^|\s)hide(?!\S)/g).test(items[i].className)) {
items[i].className = items[i].className.replace(/(?:^|\s)hide(?!\S)/g , '');
items[i].className += ' ' + 'slide-down';
}
}
I have this HTML list <ul> which contains list items with two different classes.
<ul id="Items">
<li class="sw">Switchable 1</li>
<li class="sw">Switchable 2</li>
<li class="notsw">This should remain 3</li>
<li class="sw">Switchable 4</li>
<li class="notsw">This should remain 5</li>
<li class="sw">Switchable 6</li>
</ul>
<input type="button" class="btn" value="Shuffle" />
I am trying to randomize or shuffle the order of the list items when an event is triggered (let's say a button was clicked) but only shuffle the list items with the .sw class. So far, I have achieved shuffling the list items(all of them) using a jQuery custom function. Then, I tried storing the initial indexes of the .notsw list items(I think i'm getting the right values) and used jQuery .before() to move it back after the shuffle but still I can't make it go where it should be.
Note: The list items with the .notsw class could be anywhere in the list initially.
jQuery:
$('.btn').click(function(){
var notsws = document.getElementsByClassName("notsw");
var inds = new Array();
for(var i=0; i<notsws.length; i++){
inds[i] =$('.notsw').eq(i).index();
}
$('#Items').randomize();
for(var k=0; k<notsws.length; k++){
var curr = inds[k];
$('.notsw').eq(k).before($('#Items li').eq(curr));
}
});
$.fn.randomize = function(selector){
var $elems = selector ? $(this).find(selector) : $(this).children(),
$parents = $elems.parent();
$parents.each(function(){
$(this).children(selector).sort(function(){
return Math.round(Math.random()) - 0.5;
}).remove().appendTo(this);
});
return this;
};
I HAVE A JSFIDDLE EXAMPLE HERE
I used an alternate approach. I see that you are randomizing your list and then trying to remember whether the original elements were.
Instead of that, why don't you just shuffle elements based on whether they can be shuffled.
In the sense, take an array of the indexes for the switchable elements denoted by the selector .sw and then shuffle only those indexes.
Here's how the shuffle function would look like.
function shuffle(nodes, switchableSelector) {
var length = nodes.length;
//Create the array for the random pick.
var switchable = nodes.filter("." + switchableSelector);
var switchIndex = [];
$.each(switchable, function(index, item) {
switchIndex[index] = $(item).index();
});
//The array should be used for picking up random elements.
var switchLength = switchIndex.length;
var randomPick, randomSwap;
for (var index = length; index > 0; index--) {
//Get a random index that contains a switchable element.
randomPick = switchIndex[Math.floor(Math.random() * switchLength)];
//Get the next element that needs to be swapped.
randomSwap = nodes[index - 1];
//If the element is 'switchable' continue, else ignore
if($(randomSwap).hasClass(switchableSelector)) {
nodes[index - 1] = nodes[randomPick];
nodes[randomPick] = randomSwap;
}
}
return nodes;
}
On your button click, you can simply shuffle the nodes and then re-append them to the container.
$(".btn").click(function() {
var $nodes = $("#Items").find("li");
shuffle($nodes, "sw");
$("#Items").append($nodes);
});
Working fiddle present here.
What you are doing is removing all the "sw" elements and then pushing them at the back of the list. I would create a new randomized list with the "sw" items and then add the "notsw"item at their previous indexes. You should therefore save the "notsw" indexes.
I have a function that generates a unordered list of links and I want to use javascript to select the last 2 links so that I can align them to the right.
so in the following example I would need to select the li parent of link4 and link5, then add a class so i can style it
<ul>
<li>link1</li>
<li>link2</li>
<li>link3</li>
<li>link4</li>
<li>link5</li>
</ul>
In the end it should be something like this:
<ul>
<li>link1</li>
<li>link2</li>
<li>link3</li>
<li class="align_right">link4</li>
<li class="align_right">link5</li>
</ul>
Couldn't you generate class="align_right" for the last 2 links when you build the list?
By the way, if you want to do this by javascript, you could do:
//get the sidebarmenu element
var sidebar = document.getElementById('sidebarmenu');
//getting the ul inside the wrapper
var ul = sidebar.getElementsByTagName("ul")[0];
//getting al the li childs
var li = ul.getElementsByTagName("li");
var totLi = li.length;
if(totLi >= 2){ //setting class to last 2
li[totLi-1].className = "align_right";
li[totLi-2].className = "align_right";
}
Edit: updated for your particular needs
Running example:
http://www.jsfiddle.net/steweb/m4v2J/
In Jquery:
$('ul li:last-child').prev('li').andSelf().addClass("align_right");
This best be done in the function generating those items, if you insist on client side script afterwards, here it is:
var oList = document.getElementById("myList");
var arrItems = oList.getElementsByTagName("li");
if (arrItems.length >= 2) {
arrItems[arrItems.length - 2].className = "align_right";
arrItems[arrItems.length - 1].className = "align_right";
}
For this to work, add ID to the <ul> tag and use it,
How can i make JS select every LI element inside a UL tag and put them into an array?
<div id="navbar">
<ul>
<li id="navbar-One">One</li>
<li id="navbar-Two">Two</li>
<li id="navbar-Three">Three</li>
<li id="navbar-Four">Four</li>
<li id="navbar-Five">Five</li>
</ul>
</div>
Can i make it so JS gets each of them into an array eg
navbar['0'] would return document.getElementById("navbar-One")?
You can get a NodeList to iterate through by using getElementsByTagName(), like this:
var lis = document.getElementById("navbar").getElementsByTagName("li");
You can test it out here. This is a NodeList not an array, but it does have a .length and you can iterate over it like an array.
After some years have passed, you can do that now with ES6 Array.from (or spread syntax):
const navbar = Array.from(document.querySelectorAll('#navbar>ul>li'));
console.log('Get first: ', navbar[0].textContent);
// If you need to iterate once over all these nodes, you can use the callback function:
console.log('Iterate with Array.from callback argument:');
Array.from(document.querySelectorAll('#navbar>ul>li'),li => console.log(li.textContent))
// ... or a for...of loop:
console.log('Iterate with for...of:');
for (const li of document.querySelectorAll('#navbar>ul>li')) {
console.log(li.textContent);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
<div id="navbar">
<ul>
<li id="navbar-One">One</li>
<li id="navbar-Two">Two</li>
<li id="navbar-Three">Three</li>
</ul>
</div>
QuerySelectorAll will get all the matching elements with defined selector. Here on the example I've used element's name(li tag) to get all of the li present inside the div with navbar element.
let navbar = document
.getElementById("navbar")
.querySelectorAll('li');
navbar.forEach((item, index) => {
console.log({ index, item })
});
<div id="navbar">
<ul>
<li id="navbar-One">One</li>
<li id="navbar-Two">Two</li>
<li id="navbar-Three">Three</li>
<li id="navbar-Four">Four</li>
<li id="navbar-Five">Five</li>
</ul>
</div>
If you want all the li tags in an array even when they are in different ul tags then you can simply do
var lis = document.getElementByTagName('li');
and if you want to get particular div tag li's then:
var lis = document.getElementById('divID').getElementByTagName('li');
else if you want to search a ul first and then its li tags then you can do:
var uls = document.getElementsByTagName('ul');
for(var i=0;i<uls.length;i++){
var lis=uls[i].getElementsByTagName('li');
for(var j=0;j<lis.length;j++){
console.log(lis[j].innerHTML);
}
}
var allElmnts = document.querySelectorAll("ul");
var arr = [];
arr.length = allElmnts.length;
for(var i = 0; i < allElmnts.length; i++){
arr[i] = allElmnts[i];
}