I have an array and I want to be able to divide it in 2 components, so like the first 5 elements go in one element and whatever is left go in the second element. I know I have to use slice and I can get the first component but not sure about the second one.
I have something like this:
if (item.children) {
return item.children.slice(0, 5).map((childItem) => {
const navItem = (
<Link activeClassName={styles.active} to={childItem.url}>{childItem.text}</Link>
)
return (
<li key={childItem.id}>
{navItem}
</li>
)
})
}
So how do I get another component to appear after, like
<li key={childItem.id}>
{navItem}
</li>
{navExtra}
that contains all the remaining elements from the array?
The final html should look something like this:
<ul>
<!-- // first five elements of the array -->
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<!-- // new component -->
<li>
<button>Extra</button>
<ul>
<!-- // remaning elements of the array -->
<li>Item 6</li>
<li>Item 7</li>
<!-- // etc -->
</ul>
</li>
</ul>
Variables will really help your code readability here. slice(beginIndex, endIndex) and slice(begin) are the keys.
You already know slice(0,5) will get you the first half of the array.
var x = slice(5) will get you the components of the array from the fifth element to the end. You can call map() and do any other processing on these elements to get something to display.
MSDN docs for slice
You can create an HTML element with appendChild. Here is an example of how you could do what you are trying to do:
<!DOCTYPE html>
<html>
<body>
<div id="div1">
</div>
<script>
var array = ["this", "is", "a", "random", "array", "that", "i", "just", "made"];
var splitBy = Math.round(array.length / 2);
//for first half
for (var i = 0; i <= splitBy; i++){
var li = document.createElement("li");
var node = document.createTextNode(array[i]);
li.appendChild(node);
var element = document.getElementById("div1");
element.appendChild(li);
}
//for second half
for (var i = splitBy + 1; i < array.length; i++){
var li = document.createElement("li");
var node = document.createTextNode(array[i]);
li.appendChild(node);
var element = document.getElementById("div1");
element.appendChild(li);
}
</script>
</body>
</html>
Hope this helps!
When you use splice, it alters the original array, essentially leaving you item.children with all of the elements you want to include in navExtra
We can create a return object that holds navItems and navExtra separately for you to render separately, adding the wrapping <ul> in the render, etc.
if (item.children) {
let ret = {
navItems: [],
navExtra: []
};
navItems = item.children.splice(0, 5);
ret.navItems.push(
navItems.map(navItem => {
return (
<li key={navItem.id}>
<Link activeClassName={styles.active} to={navItem.url}>{navItem.text}</Link>
</li>
)
})
)
ret.navExtra.push(
item.children.map(navExtra => {
<li key={navExtra.id}>
<Link activeClassName={styles.active} to={navExtra.url}>{navExtra.text}</Link>
</li>
})
)
}
Related
I have a dynamic list of things. How to select only 3 items (that has 2 words or less) randomly, and convert it to clickable links. So everytimes the page is reloaded, random 3 links are created. Im expecting to use something like:
innerHTML = '' + text + ''
From this:
list 1 list 2 222 list 3 list 4
list 5 555 list .. list 99 list 100
To this:
list 1 list 2 222 (*ignore 3 or more words) list 3 (*selected randomly and pointed to mysite.com/folder/list-3) list 4 (*selected randomly and pointed to mysite.com/folder/list-3)
list 5 55 (*ignore 3 or more words) list .. list 99 (*selected randomly and pointed to mysite.com/folder/list-99) list 100
Once the page has loaded, loop three times, each time grabbing a random li and refreshing that grab until it meets the criteria of having fewer than three words and not having been used yet. Each time you find an li that is valid, change the innerHTML.
window.addEventListener('load', (event) => {
for (var i = 0; i < 3; i++) {
var randomLI;
while (!randomLI || randomLI.getElementsByTagName("a").length > 0 || randomLI.innerHTML.trim().split(" ").length > 2) {
randomLI = rando(document.getElementsByTagName("li")).value;
}
randomLI.innerHTML = "" + randomLI.innerHTML + "";
}
});
<script src="https://randojs.com/1.0.0.js"></script>
<ul>
<li>list 1</li>
<li>list 2 222</li>
<li>list 3</li>
<li>list 4</li>
<li>list 5 555</li>
<li>list ..</li>
<li>list 99</li>
<li>list 100</li>
</ul>
I used randojs.com to simplify the randomness and make it more readable. If you want to use this code, make sure this is in the head tag of your html document:
<script src="https://randojs.com/1.0.0.js"></script>
You can clean up the code a bit, but below can get you started.
Try the live demo at https://codepen.io/baadaa/pen/povOJJP
<!-- Your list here. -->
<ul class="listItems">
<li>list 1</li>
<li>list 2 222</li>
<li>list 3</li>
<li>list 4</li>
<li>list 5 555</li>
<li>list ..</li>
<li>list 99</li>
<li>list 100</li>
</ul>
<!-- Button to refresh the list -->
<button class="refresh">Refresh</button>
// Store the DOM elements in variables
const ul = document.querySelector('.listItems');
const listItems = Array.from(document.querySelectorAll('.listItems li'));
// Check if three items with links are present, otherwise run the logic again
function updateList(list, pickCount) {
if (pickCount === 3) {
ul.innerHTML = list.join(' ');
} else {
randomize();
}
}
function randomize() {
let pickCount = 0;
// Iterate over the list array to transform the items
const newList = listItems.map(item => {
// 50% chance of picking the item to include link
const isPicked = Math.random() > .5 ? true : false;
// Check if the item contains two or more words
if (item.innerHTML.split(' ').length > 2 ) {
// If containing more than two words, skip.
return item.outerHTML;
} else if (isPicked) {
// If containing two or less words, and passed the random pick, transform the item
pickCount += 1;
return `<li>${item.innerHTML}</li>`;
} else {
// Otherwise, skip.
return item.outerHTML;
}
});
return updateList(newList, pickCount);
}
document.querySelector('button.refresh').addEventListener('click', randomize);
I'm trying to move all the list items from an list to another using only javascript but for some reason only half of them are actually moved.
Heres a working example of what I'm doing:
var results_ul = document.getElementById('results');
var stores_li = document.getElementsByClassName('store-list-item');
for (var x = 0; x < stores_li.length; x++) {
document.getElementById('hide').appendChild(stores_li[x]);
stores_li[x].className += ' teste';
}
<p>results</p>
<ul id="results">
<li class="store-list-item">Teste 1</li>
<li class="store-list-item">Teste 2</li>
<li class="store-list-item">Teste 3</li>
<li class="store-list-item">Teste 4</li>
</ul>
<p>Hide:</p>
<ul id="hide"></ul>
What seems to be the problem?
getElementsByClassName returns a live list.
When you append the element to a different element, you change its position in the list.
So it starts off as:
1 2 3 4
Then you move the first one:
2 3 4 1
Then you access the second one … but the second one is now 3 because everything has shuffled down the list.
You could copy each element into an array (which will not be a live list) and then iterate over that array to move them (so they won't change positions as you go).
Alternatively, you could use querySelectorAll which returns a non-live list.
You should better use querySelectorAll than getElementsByClassName
var results_ul = document.getElementById('results');
var stores_li = document.querySelectorAll('.store-list-item');
stores_li.forEach((item)=>{
document.getElementById('hide').appendChild(item);
item.className += ' teste';
});
<p>results</p>
<ul id="results">
<li class="store-list-item">Teste 1</li>
<li class="store-list-item">Teste 2</li>
<li class="store-list-item">Teste 3</li>
<li class="store-list-item">Teste 4</li>
</ul>
<p>Hide:</p>
<ul id="hide"></ul>
Try use querySelectorAll . It'll returns a non-live list. That's what you need.
var stores_li = document.querySelectorAll('.store-list-item');
To increase more information:
Live : when the changes in the DOM are reflected in the collection. The content suffers the change when a node is modified.
Non-Live : when any change in the DOM does not affect the content of the collection.
document.getElementsByClassName() is an HTMLCollection, and is live.
document.querySelectorAll() is a NodeList and is not live.
In your code you are removing each element from the first list and inserting into the new list. After you remove 2 elements it will have only 2 elements in the first list but now you are searching the 3 rd index in the loop which is not there. So to make it work i have prepended each element from the last.
var results_ul = document.getElementById('results');
var stores_li = document.getElementsByClassName('store-list-item');
var hide_ul = document.getElementById('hide');
for (var x = 0, y = stores_li.length; x < y; x++) {
hide_ul.insertBefore(stores_li[y-x-1],hide_ul.firstChild);
stores_li[x].className += ' teste';
}
<p>results</p>
<ul id="results">
<li class="store-list-item">Teste 1</li>
<li class="store-list-item">Teste 2</li>
<li class="store-list-item">Teste 3</li>
<li class="store-list-item">Teste 4</li>
</ul>
<p>Hide:</p>
<ul id="hide"></ul>
Or you may want to clone the element with Jquery and you can push into the clonned ones then delete the orginals from top. I could not find any equivalent of clone() for js but if you want to check link is here
var results_ul = document.getElementById('results');
var stores_li = document.getElementsByClassName('store-list-item');
while(stores_li.length>0) {
document.getElementById('hide').appendChild(stores_li[0]);
stores_li[x].className += ' teste';
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have a few list entries that I would like to randomly highlight, 1 item only with different background and text color.
As shown below with "list 2" highlighted.
<div id="entries">
<ul style="list-style-type: none;">
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
<li>list 4</li>
<li>list 5</li>
</ul>
</div>
How can I achieve this using vanilla javascript without the use of jQuery?
Pretty easy (vanilla JS):
// Query entries:
var entries = document.querySelectorAll('#entries ul li a');
// Clean up entries styles
for(var i = 0; i < entries.length; i++){
var style = entries[i].style;
style.backgroundColor = null;
style.color = null;
}
// Pick random index
var randomIdx = Math.floor(Math.random() * entries.length);
// Pick random entry's style
var randomEntryStyle = entries[randomIdx].style;
// Set styles
randomEntryStyle.backgroundColor = '#000';
randomEntryStyle.color = '#fff';
JS Bin
With the following code you can get a random colour for a random element's background and text.
var
elements = document.querySelectorAll('#entries ul li a'),
index = Math.floor(Math.random() * elements.length),
props = ["color", "backgroundColor"],
colour = function() {
return "#" + (Math.random() * 0xFFFFFF << 0).toString(16);
};
for (var i = 0; i < props.length; i++) elements[index].style[props[i]] = colour();
Snippet:
/* ----- JavaScript ----- */
var
elements = document.querySelectorAll('#entries ul li a'),
index = Math.floor(Math.random() * elements.length),
props = ["color", "backgroundColor"],
colour = function() {
return "#" + (Math.random() * 0xFFFFFF << 0).toString(16);
};
for (var i = 0; i < props.length; i++) elements[index].style[props[i]] = colour();
<!----- HTML ----->
<div id="entries">
<ul style="list-style-type: none;">
<li>list 1</li>
<li>list 2</li>
<li>list 3</li>
<li>list 4</li>
<li>list 5</li>
</ul>
</div>
First of all, you are going to need all these list elements inside an array to choose from. You can accomplish this by writing:
var elements = document.getElementById("entries").getElementsByTagName("li");
better however would it be, if you gave them a class name "entry", then you could do:
var elements = document.getElementsByClassName("entry")
which would be even safer, cause you can't get any nested li-elements.
Next up, we want to choose one from it randomly. You can write the following:
var chosen = elements[Math.floor(Math.random()*elements.length)];
And now just apply the css you need:
choosen.setAttribute("style", "color:white; background-color: black");
Or, to even make it cleaner, create a css class called "chosen", define the css, and apply it to the chosen element.
Greets!
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 the following....
<div class="validationbox">
<h1>Errors</h1>
<ul class="validationerrors">
<li>Error 1</li>
<li>Error 2</li>
<li>Error 3</li>
</ul>
</div>
The error list items are generated dynamically so sometimes there are none, I would like to hide the 'validationbox' div if there are no list items.
I imagine it is javascript or jquery that I should be looking at, does anyone have any examples?
In jQuery you can do it as simple as:
$(".validationbox:not(:has(li))").hide();
In pure JavaScript you need to iterate ".validationbox" elements and search for <li> nodes inside:
var div = document.getElementsByClassName("validationbox");
for (var i = 0, len = div.length; i < len; i++) {
if (!div[i].getElementsByTagName("li").length) {
div[i].style.display = "none";
}
}
$('.validationbox').toggle( $('.validationerrors li').length );
toggle() accepts a boolean value as an argument. $('.validationerrors li').length will evaluate to false if there is no <li> elements, else true, which will show the error list.
you can use not .
$('div .validationbox').not(':has(li)').hide();
hope it's help to you