Aparently shuffling an array is not so complicated:
How to randomize (shuffle) a JavaScript array?
But what if I have to (Html DOM) lists that are synchronized and I need to shuffle the order of the elements, but they should have the same final order?
For example, initial state:
<!-- List A) -->
<ul>
<li>First title</li>
<li>Second Title</li>
<li>Thrid title</li>
</ul>
<!-- List B) -->
<ul>
<li>First text</li>
<li>Second text</li>
<li>Thhird text</li>
</ul>
After shuffle:
<!-- List A) -->
<ul>
<li>Second title</li>
<li>First Title</li>
<li>Thrid text</li>
</ul>
<!-- List B) -->
<ul>
<li>Second text</li>
<li>First text</li>
<li>Third text</li>
</ul>
How can this be achieved?
Get length of items and loop through it and in loop generate random number and using generated number select an li and append it end of parent.
var ul = document.querySelectorAll("ul");
var length = ul[0].querySelectorAll("li").length;
for (var i=0; i<length; i++){
var rand = Math.floor(Math.random()*(length));
ul.forEach(function(ele){
ele.appendChild(ele.querySelectorAll("li")[rand]);
});
}
<ul>
<li>First title</li>
<li>Second Title</li>
<li>Thrid title</li>
</ul>
<ul>
<li>First text</li>
<li>Second text</li>
<li>Thhird text</li>
</ul>
Also you can use jQuery to write less code
var $ul = $("ul:first li");
$ul.each(function(){
var rand = Math.floor(Math.random()*$ul.length);
$("ul").each(function(i, ele){
$("li", ele).eq(rand).appendTo(ele);
});
});
$("button").click(function(){
var $ul = $("ul:first li");
$ul.each(function(){
var rand = Math.floor(Math.random()*$ul.length);
$("ul").each(function(i, ele){
$("li", ele).eq(rand).appendTo(ele);
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click</button>
<ul>
<li>First title</li>
<li>Second Title</li>
<li>Thrid title</li>
</ul>
<ul>
<li>First text</li>
<li>Second text</li>
<li>Thhird text</li>
</ul>
You can easily shuffle an DOM html in various number of ways. One way that is easy to understand is to convert the DOM elements into an array, then randomly splice each element out of its parent array and append them back to the parent DOM element.
In my snippet below, I used jQuery to make the code easier to read, but it can be done with native javascript just as well.
$('#shuffle').click(function(){
var items = []; // start with an empty array
$('#list_a li').each(function(i,d){
items.push(d); // add all li items into the array in their current order
});
$('#list_a').html(''); // clear the ul list
// execute this loop as many times as items.length
for(var i=items.length-1; i>=0; i--) {
var r = Math.floor(Math.random()*items.length); // pick a random array position
var item = items.splice(r,1); // take that item out of the array
$('#list_a').append(item); // append it back to the ul
}
});
ul
{
background-color: #def;
display: inline-block;
border: 1px solid grey;
padding: 30px;
border-radius: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<b>List A</b>
<ul id="list_a">
<li>First Item</li>
<li>Second Item</li>
<li>Third Item</li>
<li>Fourth Item</li>
<li>Fifth Item</li>
</ul>
<button id="shuffle">Shuffle</button>
Related
This question already has answers here:
How to add a list item to an existing unordered list
(14 answers)
Closed 5 years ago.
I have list:
<ul class="customList">
<li data-featured="1">First item</li>
<li data-featured="0">Another item</li>
<li data-featured="0">Last item..</li>
</ul>
How can I insert my custom <li> after featured="1"? If all featured 0, then insert as first li.. Thanks in advance.
var self = this;
self.scope.find('.customList').prepend(myCustomItem); //this code inserts in begin, not after featured items..
$(myCustomItem).insertAfter('[data-featured="1"]'); //I've tried to build something.. but this fails
Sorry for my bad English
EDIT
myCustomItem.insertAfter('[data-featured="1"]'); looks like is working, but when there is any data-featured=1, then nothing happens. Any suggestions?
Use the jQuery attribute selector [attribute=value]:
$(" <li data-featured='222'>NEW ITEM</li>").insertAfter('.customList li[data-featured="1"]');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="customList">
<li data-featured="1">First item</li>
<li data-featured="0">Another item</li>
<li data-featured="0">Last item..</li>
</ul>
Or if you want to insert it after the first li tag the, use this:
$(" <li data-featured='222'>NEW ITEM</li>").insertAfter('.customList li:first');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="customList">
<li data-featured="1">First item</li>
<li data-featured="0">Another item</li>
<li data-featured="0">Last item..</li>
</ul>
You can first use the jQuery attribute selector [attribute=value]: to check if li with data-featured="1" is exist or not. If exist then use the jQuery method .insertAfter() to insert the new li after it and if not then insert new li at the first position using .insertBefore().
if($('.customList li[data-featured="1"]').length>0)
{
$("<li data-featured='99'>NEW ITEM</li>").insertAfter('.customList li[data-featured="1"]');
}
else
{
$("<li data-featured='99'>NEW ITEM</li>").insertBefore('.customList li:first');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="customList">
<li data-featured="0">First item</li>
<li data-featured="0">Another item</li>
<li data-featured="0">Last item..</li>
</ul>
First of all i would say don't use append() when the question is about insert after an li. Check below snippet, first check the condition for data-featured=1 available or not and then prepend() or insertAfter() based on that.
var flag = false;
$('.customList li').each(function() {
if ($(this).data('featured') == '1') {
flag = true;
}
});
if (flag) {
$("<li data-featured='222'>NEW ITEM</li>").insertAfter('.customList li[data-featured="1"]');
} else {
$('.customList').prepend(" <li data-featured='222'>NEW ITEM</li>");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="customList">
<li data-featured="1">First item</li>
<li data-featured="0">Another item</li>
<li data-featured="0">Last item..</li>
</ul>
newStatus.insertAfter('[data-featured="1"]');
Works fine but just have to check for the case when the item added is the first item or not. That can be easily done by if condition.
something like this:
if($('li[data-featured="1"]') != null)
here is the example: Link
CMS, that I am using, generates such a code for menu:
<ul>
<li>second level menu</li>
<li>third level menu</li>
<li>third level menu</li>
</ul>
<ul>
<li>second level menu</li>
<li>third level menu</li>
</ul>
...
I would like to change the way of displaying the third level menu. Is it possible to change this code using javascript into:
<ul>
<li>second level menu
<ul>
<li>third level menu</li>
<li>third level menu</li>
</ul>
</li>
</ul>
<ul>
<li>second level menu
<ul>
<li>third level menu</li>
</ul>
</li>
</ul>
...
You need to differentiate li elements, so you can identify then, which should be first or next level elements. Setting data-attributes, I think is a not "so much bad solution".
Btw, below is only a boilerplate code, and will work verily for given structure. You need to grasp the logic - so you can apply it to other problems.
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<ul>
<li data-level="2">second level menu</li>
<li data-level="3">third level menu</li>
<li data-level="3">third level menu</li>
</ul>
<ul>
<li data-level="2">second level menu</li>
<li data-level="3">third level menu</li>
</ul>
<script type="text/javascript">
var uls = document.querySelectorAll('ul');
for(var i=0; i<uls.length; i++) {
var children = uls[i].querySelectorAll("li");
var _children = Array.from(children).filter(e=> e.dataset.level == "3");
if (_children.length > 0) {
var newul = document.createElement("ul");
_children.map(e=>newul.appendChild(e));
uls[i].appendChild(newul);
}
}
</script>
</body>
</html>
I have a navigation that looks like this:
<div class="sub_navigation">
<ul>
<li>First Level
<ul>
<li>Second Level</li>
<li>Second Level
<ul>
<li>Third Level</li>
</ul>
</li>
</ul>
</li>
<li>First Level
<ul>
<li>Second Level</li>
<li>Second Level
<ul>
<li>Third Level
<ul>
<li>Fourth Level</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
Basically, everly li can have an infinite amount of children uls. I need to get the number of the highest level
For example, right now I'd be looking for the number 4 as the second li has 4 Levels.
I tried two different methods:
First, my own attempt that failed miserably:
$count = 1;
$('.sub_navigation > .menu > li').each(function() {
countChildren($count);
});
function countChildren() {
$(this).children('ul').each(function() {
$count++;
console.log($count);
});
return($count);
}
Second, a way I took from stack overflow but that didn't work out for me either, only returning 0 everytime:
var arr = []; //populate the length of children into this array.
$('.sub_navigation ul').map(function (i) {
arr[i] = $(this).children('ul').length;
});
var maxValue = Math.max.apply(Math, arr); //get the max value from the array
console.log(maxValue);
Could someone please point me in the right direction? I sadly have no idea how to acieve this at all anymore.
Thanks a lot!
You can use parentsUntil() like
var temp = $('.sub_navigation li').map(function() {
return $(this).parentsUntil('.sub_navigation', 'ul').length;
}).get();
var max = Math.max.apply(Math, temp);
snippet.log('Count: ' + max)
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="sub_navigation">
<ul>
<li>First Level
<ul>
<li>Second Level</li>
<li>Second Level
<ul>
<li>Third Level</li>
</ul>
</li>
</ul>
</li>
<li>First Level
<ul>
<li>Second Level</li>
<li>Second Level
<ul>
<li>Third Level
<ul>
<li>Fourth Level
<ul>
<li>Fifth Level</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
Instead of going from top to bottom level you could do it the other way.
Check for each li how many ul ancestors it has
var ulAncestorsPerLi = $( '.sub_navigation li' ).map(function(){
return $(this).parentsUntil( '.sub_navigation', 'ul' ).length;
}).get(),
deepestLevel = Math.max.apply( Math, ulAncestorsPerLi );
alert( deepestLevel );
var ulAncestorsPerLi = $('.sub_navigation li').map(function() {
return $(this).parentsUntil('.sub_navigation', 'ul').length;
}).get(),
deepestLevel = Math.max.apply(Math, ulAncestorsPerLi);
alert(deepestLevel);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="sub_navigation">
<ul>
<li>First Level
<ul>
<li>Second Level</li>
<li>Second Level
<ul>
<li>Third Level</li>
</ul>
</li>
</ul>
</li>
<li>First Level
<ul>
<li>Second Level</li>
<li>Second Level
<ul>
<li>Third Level
<ul>
<li>Fourth Level</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
How can I modify the below code so that I get only the parent li within the first ul and then with another variable get the sub li of the given sub ul?
$(function () {
var vULSize = $("ul li").size();
console.log(vULSize);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<ul>
<li>First</li>
<li>Second
<ul>
<li>Second Sub 1</li>
<li>Second Sub 2</li>
</ul>
</li>
<li>Third</li>
<li>Third
<ul>
<li>Third Sub 1</li>
<li>Third Sub 2</li>
</ul>
</li>
</ul>
Fiddle: http://jsfiddle.net/0sp9pohr/
You can ignore all li's that are descendants of an li like
$(function() {
var vULSize = $("ul li:not(li li)").size();
snippet.log(vULSize);
});
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>First</li>
<li>Second
<ul>
<li>Second Sub 1</li>
<li>Second Sub 2</li>
</ul>
</li>
<li>Third</li>
<li>Third
<ul>
<li>Third Sub 1</li>
<li>Third Sub 2</li>
</ul>
</li>
</ul>
First you need to wrap it all in something, say for instance a div with the id container. Then you can do something like this:
var vULSize = $("#container > ul > li").length;
The > selector gives you only the direct descendants.
If you want to count the number of list elements one level down, this will give you an array with the result:
var counts = $("#container > ul > li").map(function() {
return $(this).find("li").length
}).get();
As pointed out in comments, you should use .length instead of .size().
With jQuery I can add consecutive numeric classes to list items like so:
$(".item-list li").each(function (i) {
$(this).addClass("item-list-" + i);
});
So with an HTML structure as such:
<ul class="item-list">
<li>an item</li>
<li>another item</li>
<li>another item</li>
<li>another item</li>
<li>another item</li>
</ul>
... this will output nice list item classes in numeric order:
class="item-list-0"
class="item-list-1"
... etc...
What I'm trying to do is number of to 4 items and then start the numbering over at zero again. I figured I'd have to use nth child within my numeric + i code but I just have not been able to find any documentation on this. Everything that I've found relates to finding an nth element but not starting a count over after a specified amount.
you can do this using modulus operator itself:
$(".item-list li").each(function (i) {
$(this).addClass("item-list-" + (i%4));
});
Output markup
<ul class="item-list">
<li class="item-list-0">an item</li>
<li class="item-list-1">another item</li>
<li class="item-list-2">another item</li>
<li class="item-list-3">another item</li>
<li class="item-list-0">another item</li>
<li class="item-list-1">another item</li>
<li class="item-list-2">another item</li>
<li class="item-list-3">another item</li>
<li class="item-list-0">another item</li>
</ul>
Demo