Sort nested divs alphabetically - javascript

I have the following div layout:
<div class="container">
<div class="entry">
<div class="title">World</div>
<div class="description">text1</div>
</div>
<div class="entry">
<div class="title">hello</div>
<div class="description">text2</div>
</div>
<div class="entry">
<div class="title">Lorem</div>
<div class="description">text3</div>
</div>
</div>
I want to sort the entry divs alphabetically by the content of the child title div.
What I've tried so far
This will sort it alphabetically:
var alphabeticallyOrderedDivs = $('.title').sort(function(a, b) {
return String.prototype.localeCompare.call($(a).text().toLowerCase(), $(b).text().toLowerCase());
});
var container = $(".container");
container.detach().empty().append(alphabeticallyOrderedDivs);
$('body').append(container);
But it will strip the description divs. Try jsFiddle demo.
How can I sort the divs alphabetically without stripping anything?

Your logic is correct, but the issue is you're sorting the .title elements. Instead you need to sort the .entry elements, and then find the .title within the current .entry and perform the comparison on their text values. Try this:
var alphabeticallyOrderedDivs = $('.entry').sort(function(a, b) {
var $aTitle = $(a).find('.title'), $bTitle = $(b).find('.title');
return String.prototype.localeCompare.call($aTitle.text().toLowerCase(), $bTitle.text().toLowerCase());
});
var container = $(".container");
container.detach().empty().append(alphabeticallyOrderedDivs);
$('body').append(container);
.entry {
border: 1px solid #CCC;
padding: 5px;
margin: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<div class="container">
<div class="entry">
<div class="title">World</div>
<div class="description">text1</div>
</div>
<div class="entry">
<div class="title">hello</div>
<div class="description">text2</div>
</div>
<div class="entry">
<div class="title">Lorem</div>
<div class="description">text3</div>
</div>
</div>

How can I sort the divs alphabetically without stripping anything?
By sorting the right elements?
Why are you sorting .title, if as you stated the .entry elements are what you actually want to sort?
var alphabeticallyOrderedDivs = $('.entry').sort(function(a, b) {
return String.prototype.localeCompare.call(
$(a).find('.title').text().toLowerCase(),
$(b).find('.title').text().toLowerCase());
});
http://jsfiddle.net/yapu9a6m/1/

You just have to take the parent element with you when you sort. Then you go searching the child element after which you want to sort:
var alphabeticallyOrderedDivs = $('.entry').sort(function(a, b) {
return String.prototype.localeCompare.call($(a).find("div.title").text().toLowerCase(), $(b).find("div.title").text().toLowerCase());
});

You're sorting the titles, you should be sorting the entries.
So 1 tiny change:
var alphabeticallyOrderedDivs = $('.entry').sort(function(a, b) {
return String.prototype.localeCompare.call($(a).text().toLowerCase(), $(b).text().toLowerCase());
});
See updated fiddle

Related

How to get div elements between a range of numbers?

I want to get elements between a range of numbers, let's say between 10 and 15 from these:
<div class="1">....</div>
<div class="2">....</div>
<div class="3">....</div>
...
...
...
<div class="20">....</div>
Desired Output:
<div class="10">....</div>
<div class="11">....</div>
<div class="12">....</div>
<div class="13">....</div>
<div class="14">....</div>
<div class="15">....</div>
While the JS solution is probably better, mainly because it's more flexible, I want to mention the CSS solution, even though it has more limitations.
#parent {
display: inline-block;
}
div {
border: 1px solid #333;
padding: 5px;
}
div div {
width: 50px;
}
div:nth-child(n+10):nth-child(-n+15) { /* From the 10th child up to the 15th child */
background-color: green;
}
<div id="parent">
<div>01</div>
<div>02</div>
<div>03</div>
<div>04</div>
<div>05</div>
<div>06</div>
<div>07</div>
<div>08</div>
<div>09</div>
<div>10</div>
<div>11</div>
<div>12</div>
<div>13</div>
<div>14</div>
<div>15</div>
<div>16</div>
<div>17</div>
<div>18</div>
<div>19</div>
<div>20</div>
</div>
By combining two nth-child selectors you can specify a range of elements.
You can use this selector in JS as well
document.querySelectorAll('div:nth-child(n+10):nth-child(-n+15)');
The downside is that it will work well only if all the elements are siblings and the only children of the parent element.
Just use slice will be sufficient.
[...document.getElementsByTagName("div")].slice(10,5)
[...document.querySelectorAll('div')]
.filter((e) => Number(e.classList.value) >= 10)
.filter((e) => Number(e.classList.value) <= 15)
.map((e) => console.log(e.outerHTML))
// <div class="10">....</div>
// <div class="11">....</div>
// <div class="12">....</div>
// <div class="13">....</div>
// <div class="14">....</div>
// <div class="15">....</div>
The following example only works with this specific case where the classes are numbered. This is not a common scenario. Most would use unique IDs and then common Classes for grouping.
$(function() {
function selectBetween(start, finish) {
var selects = [];
for (start; start <= finish; start++) {
selects.push("." + start);
}
return $(selects.join(", "));
}
var myDivs = selectBetween(10, 15);
console.log(myDivs.length);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="1">....</div>
<div class="2">....</div>
<div class="3">....</div>
<div class="4">....</div>
<div class="5">....</div>
<div class="6">....</div>
<div class="7">....</div>
<div class="8">....</div>
<div class="9">....</div>
<div class="10">....</div>
<div class="11">....</div>
<div class="12">....</div>
<div class="13">....</div>
<div class="14">....</div>
<div class="15">....</div>
<div class="16">....</div>
<div class="17">....</div>
<div class="18">....</div>
<div class="19">....</div>
<div class="20">....</div>
Console will show 6.
Just map over an array of length of a number of elements required and use getElementsByClassName method to get the required elements between the range.
const getElements = (min, max) => {
// construct an array of length of number of elements b/w min & max
// and then map the required elements with the className selector
return [...new Array((max - min) + 1)]
.map((_, index) => document.getElementsByClassName(index + min));
}
console.log(getElements(10, 15));
<div class="0">0</div>
<div class="1">1</div>
<div class="2">2</div>
<div class="3">3</div>
<div class="4">4</div>
<div class="5">5</div>
<div class="6">6</div>
<div class="7">7</div>
<div class="8">8</div>
<div class="9">9</div>
<div class="10">10</div>
<div class="11">11</div>
<div class="12">12</div>
<div class="13">13</div>
<div class="14">14</div>
<div class="15">15</div>
<div class="16">16</div>
<div class="17">17</div>
<div class="18">18</div>
<div class="19">19</div>
<div class="20">20</div>
In order to get required output, first I would fetch all divs on the page,
convert resulting nodelist into arrays so that I can apply arrays' filter
method and then I would populate another div with outer HTML of the filtered
arrays.
Here, I have created an arrow function which does filtering using parameters
to make filter reusable. The code reads as under:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Get Div Elements from n number to n number</title>
<script>
window.onload = ()=>{
// get all divs on this page
let allDivs = document.querySelectorAll('div')
// arrow function to get required divs where
// start is int, stop is int and divs is nodelist
const getDivsBetween = (start, stop, divs)=>{
// convert nodelist to JavaScript array
let theDivs = Array.from(divs)
// filter the array
let retVal = theDivs.filter((element)=>{
// convert className to integer for checking
// whether className is between the values
let className = parseInt(element.className)
// return selected element
if (className >= start && className <=stop) return element
})
return retVal // return array containing filtered elements
}
// get required divs
let outPutDivs = getDivsBetween(10, 15, allDivs)
// get div where output will go
let theOutputDiv = document.getElementById('output')
// populate output div with filtered divs outer html
// to get required output
outPutDivs.map((element)=>{
let thisDiv = document.createElement('div')
thisDiv.append(element.outerHTML)
theOutputDiv.append(thisDiv)
})
}
</script>
</head>
<body>
<div>Here are original divs</div>
<div id="originals">
<div class="1">....</div>
<div class="2">....</div>
<div class="3">....</div>
<div class="4">....</div>
<div class="5">....</div>
<div class="6">....</div>
<div class="7">....</div>
<div class="8">....</div>
<div class="9">....</div>
<div class="10">....</div>
<div class="11">....</div>
<div class="12">....</div>
<div class="13">....</div>
<div class="14">....</div>
<div class="15">....</div>
<div class="16">....</div>
<div class="17">....</div>
<div class="18">....</div>
<div class="19">....</div>
<div class="20">....</div>
</div>
<hr>
<div>Here is required output</div>
<div id="output"></div>
</body>
</html>

Remove elements with same data-id

I have a pattern like that:
<section>
<div data-id="39"></div>
<div data-id="31"></div>
<div data-id="57"></div>
<div data-id="10"></div>
<div data-id="27"></div>
<div data-id="5"></div>
<div data-id="89"></div>
</section>
That contains some data that are live updated via AJAX. Sometimes, it may happen to receive from the server a data updated with the same id of another one in the section, and since it's the same id, I need to remove the old data to avoid multiple datas and keep just the updated one.
For example, I receive an update with data-id 27 and I insert at the top:
<section>
<div data-id="27"></div>
<div data-id="39"></div>
<div data-id="31"></div>
<div data-id="57"></div>
<div data-id="10"></div>
<div data-id="27"></div>
<div data-id="5"></div>
<div data-id="89"></div>
</section>
After inserted it, how can I do a check that if 27 is already available in the section (so the last iteration), remove it from the section? Basically removing all the data with the same id and keep just the one at the top.
Not very inspired at the moment but with the amount of info you game us i made this example with jquery. It can be done also just with plain javaScript if needed
$('div').each(function() {
dataId = $(this).data('id');
otherDataId = $(this).siblings().data('id');
if (otherDataId === dataId) {
$(this).hide()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<div data-id="27">27</div>
<div data-id="39">1</div>
<div data-id="31">2</div>
<div data-id="57">3</div>
<div data-id="10">4</div>
<div data-id="27">27</div>
<div data-id="5">5</div>
<div data-id="89">6</div>
</section>
You could also try creating a function to remove the element with the specific data-id value you want like this:
const removeItems = (number) => {
let elements = document.querySelectorAll(`div[data-id="${number}"]`);
elements.forEach((e) => { e.remove() });
};
And then to remove elements with data-id=27 you can do: removeItems(27);.
Take a look:
const removeItems = (number) => {
let elements = document.querySelectorAll(`div[data-id="${number}"]`);
elements.forEach((e) => { e.remove() });
};
removeItems(27);
<section>
<div data-id="27">27</div>
<div data-id="39">39</div>
<div data-id="31">31</div>
<div data-id="57">57</div>
<div data-id="10">10</div>
<div data-id="27">27</div>
<div data-id="5">5</div>
<div data-id="89">89</div>
</section>
document.querySelector(`[data-id=${myDataId}]`).remove();

Get next element with class which is not sibling

I want to traverse through pages and toggle active class through them. How should I do this without using set class?
HTML
<div class="page active"></div>
<div class="set">
<div class="page"></div>
<div class="page"></div>
</div>
<div class="page"></div>
jQuery
$('.active').toggleClass('active').toggle().nextAll('.page').toggleClass('active');
I am assuming that by "traverse" you mean you want to toggle the .page divs one by one in a certain order.
If that is the case, write an algorithm that traverses a tree: given a root, toggle if it is a .page, and recursively deal with each of its children
function traverse(root){
if(!root) return;
if(root.hasClass('page')) root.toggle('active');
root.children().forEach(function(child){
traverse(child);
});
//lets say you want to bind to click event on every div
$('div').click(function(){
traverse($(this));
});
}
Unfortunately we don't have a direct way to find the next non sibling element, but we can handle that situation in many ways using jquery functions. I just tried on way to achieve your goal, check out this working fiddle and let me know if you need any clarity, added some inline comments also for your understanding.
HTML:
<div class="page active">div 1</div>
<div class="page">div 2</div>
<div class="set">
<div class="page">set 1 - div 1</div>
<div class="page">set 1 - div 2</div>
<div class="page">set 1 - div 3</div>
</div>
<div class="page">div 5</div>
<div class="set">
<div class="page">set 2 - div 1</div>
<div class="page">set 2 - div 2</div>
</div>
<div class="page">div 6</div>
<button class="next-btn">Next</button>
CSS:
.active {
color: red;
}
.next-btn {
cursor: pointer;
}
Javascript:
$(function() {
$('button').click(function() {
var elem = $(".page.active").toggleClass('active'); // current active element
var nextElem = elem.next(); // next element
// go above one level and get next element
// if no next element exists, means end of the child level
if (!nextElem.length) {
nextElem = elem.parent().next();
}
// if next element has some PAGE children then update the first child element
if (nextElem.children('.page').length > 0 ) {
nextElem.children('.page:first-child').toggleClass('active');
} else if (nextElem.hasClass('page')) {
nextElem.toggleClass('active');
}
});
});
This approach handles the scenario with one child level, you can extend this to multiple levels also with recursive functions, I think this helps you to handle your scenario accordingly.
Working fiddle
You could achieve that using indexes to get the next element in the DOM using the index of active one +1 then active it, I think the following is what you are looking for:
var getActiveIndex = function(){
var active_index;
$('.page').each(function(i){
if ( $(this).hasClass('active') )
active_index = i;
})
return active_index;
}
$('body').on('click', '.next', function(){
var active_page_index = getActiveIndex(); //Get active page index
var new_index = active_page_index+1; //Set the next page index
var next_page = $('.page:eq('+new_index+')'); //Get the next page
$('.page').removeClass('active');
if(next_page.length)
next_page.addClass('active');
else
$('.page:first').addClass('active');
})
I hope this helps.
var getActiveIndex = function(){
var active_index;
$('.page').each(function(i){
if ( $(this).hasClass('active') )
active_index = i;
})
return active_index;
}
$('body').on('click', '.next', function(){
var active_page_index=getActiveIndex();
var new_index = active_page_index+1;
var next_page = $('.page:eq('+new_index+')');
$('.page').removeClass('active');
if(next_page.length)
next_page.addClass('active');
else
$('.page:first').addClass('active');
})
.active{
background-color: red;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="page active">page</div>
<div class="set">
<div class="page">- Set page</div>
<div class="page">- Set page</div>
</div>
<div class="page">page</div>
<div class="page">page</div>
<div class="page">page</div>
<div class="set">
<div class="page">- Set page</div>
<div class="page">- Set page</div>
</div>
<div class="page">page</div>
<br>
<button class='next'>Active the next page</button>

Wrap two divs with different class

I've these divs
<div class="col_1_6"></div>
<div class="col_1_4"></div>
<div class="col_1_6"></div>
<div class="col_1_4"></div>
<div class="col_1_4"></div>
<div class="col_1_6"></div>
I want whatever the order of the divs, wrap col_1_4 and col_1_6 in a div called row_content
<div class="row_content">
<div class="col_1_6"></div>
<div class="col_1_4"></div>
</div>
<div class="row_content">
<div class="col_1_4"></div>
<div class="col_1_6"></div>
</div>
I already try this :
$('.col_1_6, .col_1_4').wrapAll('<div class="row"></div>')
But it wrap all the divs not each two divs.
Thanks for the help.
You can select all your divs and then do a for loop that increment by 2 every iteration.
With the index of the loop, you can then use .slice on the jQuery element and that wrap your divs.
var $divs = $('.col_1_6, .col_1_4');
for(var i = 0; i < $divs.length; i+=2){
$divs.slice(i, i+2).wrapAll('<div class="row"></div>');
console.log($divs);
}
.row{
background: red;
margin : 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="col_1_6">1</div>
<div class="col_1_4">2</div>
<div class="col_1_6">3</div>
<div class="col_1_4">4</div>
<div class="col_1_4">5</div>
<div class="col_1_6">6</div>
I've found this method which works
$(".col_1_6").each(function(index) {
$(this).next(".col_1_4").andSelf().wrapAll("<div class='row' />")
});
but this only work if all div are in this order .col_1_6 > .col_1_4 and not .col_1_4 > col_1_6 as in your html
http://jsfiddle.net/mrjx9dav/11/
Another not-so-pretty solution that wraps every 2 divs that has the class starting with col_1.
$('div[class^="col_1"]').each(function(ind) {
ind%2 === 0 && ($(this).add($(this).next()).wrapAll('<div class="row"></div>'));
});
-Demo-

wrap more than one nodes with div

How can i wrap exactly half of the div's with another div using jquery or javascript
I have this
<div class="post">1</div>
<div class="post">2</div>
<div class="post">3</div>
<div class="post">4</div>
<div class="post">5</div>
<div class="post">6</div>
I want this
<div class="wrap">
<div class="post">1</div>
<div class="post">2</div>
<div class="post">3</div>
</div>
<div class="wrap">
<div class="post">4</div>
<div class="post">5</div>
<div class="post">6</div>
</div>
Try using this:
var posts = $('.post'),
postCount = posts.length,
postHalf = Math.round(postCount/2),
wrapHTML = '<div class="wrap"></div>';
posts.slice(0, postHalf).wrapAll(wrapHTML); // .slice(0, 3)
posts.slice(postHalf, postCount).wrapAll(wrapHTML); // .slice(3, 6)
This selects all .post, gets the number of elements found then halves that value to get the splitting point. It then uses .slice() to select a specific range of elements and .wrapAll() to wrap each selection in <div class="wrap"></div>.
Here it is working: http://jsfiddle.net/ekzrb/

Categories