Sort Divs based on distant child's value? - javascript

I have some product listings on a third-party site. All listings are contained in .listings-list. I'm trying to sort each .listing by the value of the #BGvalue div 4 children down in ascending order.
If it matters each div generally has 2-3 divs in it and the total listings can be up to 100 per page. I'm looking to make a button that I can press to do the sorting based on #BGvalue.
I'm trying to follow this tutorial and adapt it to my needs: https://riptutorial.com/jquery/example/11477/sorting-elements so my variables don't make sense since I just plopped my values in.
Also, I have jQuery loading so it isn't included here but it works.
I've tried this and it doesn't currently work. Some previous iteration appended just the actual BGValue to the bottom but there was no sort. I've since lost that code.
var ascending = true;
var $myColorList= $('#root > div > div > div.marketplace-panel-with-scrollbars > div > div:nth-child(1) > div > div.listings-area > div.listings-list');
var $colors = $myColorList.find( "#root > div > div > div.marketplace-panel-with-scrollbars > div > div:nth-child(1) > div > div.listings-area > div.listings-list > div:nth-child(1)" );
var sortList = Array.prototype.sort.bind($colors);
var doSort = function ( ascending ) {
sortList(function ( a, b ) {
var aText = a.find('#BGvalue')
var bText = b.find('#BGvalue')
if ( aText < bText ) {
return ascending ? -1 : 1;
}
if ( aText > bText ) {
return ascending ? 1 : -1;
}
return 0;
});
$myColorList.append($colors);
};
doSort(ascending);
//tutorial https://riptutorial.com/jquery/example/11477/sorting-elements
//main area holding items #root > div > div > div.marketplace-panel-with-scrollbars > div > div:nth-child(1) > div > div.listings-area > div.listings-list
//items to sort #root > div > div > div.marketplace-panel-with-scrollbars > div > div:nth-child(1) > div > div.listings-area > div.listings-list > div:nth-child(1)
//sort by #BGvalue
Here is sample code:
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper>
<div class="listing-bg">
<div class"listing-info">
<div id="BGvalue">3</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper>
<div class="listing-bg">
<div class"listing-info">
<div id="BGvalue">111</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper>
<div class="listing-bg">
<div class"listing-info">
<div id="BGvalue">80</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper>
<div class="listing-bg">
<div class"listing-info">
<div id="BGvalue">1000</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
</div>
Here is the expected outcome
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper>
<div class="listing-bg">
<div class"listing-info">
<div id="BGvalue">3</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper>
<div class="listing-bg">
<div class"listing-info">
<div id="BGvalue">80</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper>
<div class="listing-bg">
<div class"listing-info">
<div id="BGvalue">111</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper>
<div class="listing-bg">
<div class"listing-info">
<div id="BGvalue">1000</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
</div>

Several issues:
The given HTML has unclosed quotes at each occurrence of listing-bg-wrapper
It has unclosed div tags at the top level.
It has duplicate id values ("BGvalue"). This is invalid in HTML. You should use a class attribute instead.
The code seems to assume that $colors has elements that share the same parent and that they can be appended to a single container $myColorList. But the truth is that $myColorList is not one element, and each has its own child. So if you want to sort this list, you'll have to sort the elements in $myColorList, not $colors. This means the container of the list is the parent of $myColorList. So I propose you use those jQuery variables for what is 1 level higher in the DOM tree.
a.find will not work, as a (and b) is not a jQuery object, but a DOM element. So you'll need to first make the jQuery collection with $(a).
find returns a jQuery collect, not a text, so comparing like aText < bText will not have the desired effect. You'll need to first get the text, with .text() and then convert that text to number.
Not an error, but there is a much easier way to determine the return value of the comparator function, using subtraction.
Here is the corrected HTML (with an additional outer div) and JavaScript:
// Simplified selector so it works with the demo HTML:
// .listings-list are the elements to be sorted, not their children.
var $colors = $('div.listings-list');
var $myColorList = $colors.parent(); // Container is one level up
var sortList = Array.prototype.sort.bind($colors);
function doSort(ascending) {
sortList(function (a, b) {
// Convert to jQuery, get the `.text()` and convert to number (+)
// Using class selector, no duplicate ID allowed!
var aText = +$(a).find('.BGvalue').text();
var bText = +$(b).find('.BGvalue').text();
// Simple way to define the return value
return ascending ? aText - bText : bText - aText;
});
$myColorList.append($colors);
};
doSort(true);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div> <!-- added this DIV -->
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper">
<div class="listing-bg">
<div class"listing-info">
<!-- Use class. No duplicate ID allowed -->
<div class="BGvalue">3</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper">
<div class="listing-bg">
<div class"listing-info">
<div class="BGvalue">111</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper">
<div class="listing-bg">
<div class"listing-info">
<div class="BGvalue">80</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
</div>
<div class="listings-list">
<div class="listing">
<div class="listing-bg-wrapper">
<div class="listing-bg">
<div class"listing-info">
<div class="BGvalue">1000</div>
</div>
<div class"listing-info2"></div>
<div class"listing-info3"></div>
</div>
</div>
</div>
</div>
</div>

Related

How to display only the highest "score" according to the text content inside each div?

I would like to archive the below by using JavaScript (or with jQuery). Here is the HTML structure:
<div class="score-set">
<div class="score-item">A<div id="score">96+</div></div>
<div class="score-item">B<div id="score">99</div></div>
<div class="score-item">C<div id="score">99</div></div>
<div class="score-item">D<div id="score">96-</div></div>
</div>
<div class="score-set">
<div class="score-item">A<div id="score">86</div></div>
<div class="score-item">B<div id="score">88</div></div>
<div class="score-item">C<div id="score">90</div></div>
<div class="score-item">D<div id="score">90+</div></div>
</div>
<div class="score-set">
<div class="score-item">A<div id="score">83-</div></div>
<div class="score-item">B<div id="score">83+</div></div>
<div class="score-item">C<div id="score">76</div></div>
<div class="score-item">D<div id="score">78</div></div>
</div>
The JavaScript will do the modification, and the desired results will be B 99 C90 A 83- , which looks like:
<div class="score-set">
<div class="score-item">B<div id="score">99</div></div>
</div>
<div class="score-set">
<div class="score-item">C<div id="score">90</div></div>
</div>
<div class="score-set">
<div class="score-item">A<div id="score">83-</div></div>
</div>
The rules are:
Ignore all non-number in id="score", eg. + and -, and do the ranking.
Show one highest score item.
If two score items are the same in a set, show just one according to the div item sequence inside <div class="score-set">, ie. in the above example A > B > C > D.
When writing the result, write the original div item, including + or -.
To be able to do this, it would be best to get each individual score-set and treat one after another.
For each score item, we need to first get the score and transform it (Array#map) into a number with no digits (.replace(\/D+/g, ''))and memorize the score item html object.
Number(scoreItem.querySelector('div').innerText.replace(/\D+/g, ''))
We can then sort the remaining ones in descending order and simply take the first one of the list. Can be done with Array#sort and destructuring assignment.
.sort(({ score: scoreA }, { score: scoreB }) => scoreB - scoreA)
Then finally we update the score set html.
scoreSet.innerHTML = '';
scoreSet.appendChild(scoreItem);
const scoreSets = document.getElementsByClassName('score-set');
for(const scoreSet of scoreSets){
const [{ scoreItem }] = Array
.from(scoreSet.getElementsByClassName('score-item'), scoreItem => ({
scoreItem,
// it would be better here to access the score using the id
// but `score` is used multiple times which makes getting
// the score element unreliable
score: Number(scoreItem.querySelector('div').innerText.replace(/\D+/g, ''))
}))
.sort(({ score: scoreA }, { score: scoreB }) => scoreB - scoreA)
scoreSet.innerHTML = '';
scoreSet.appendChild(scoreItem);
}
<div class="score-set">
<div class="score-item">A
<div id="score">96+</div>
</div>
<div class="score-item">B
<div id="score">99</div>
</div>
<div class="score-item">C
<div id="score">99</div>
</div>
<div class="score-item">D
<div id="score">96-</div>
</div>
</div>
<div class="score-set">
<div class="score-item">A
<div id="score">86</div>
</div>
<div class="score-item">B
<div id="score">88</div>
</div>
<div class="score-item">C
<div id="score">90</div>
</div>
<div class="score-item">D
<div id="score">90+</div>
</div>
</div>
<div class="score-set">
<div class="score-item">A
<div id="score">83-</div>
</div>
<div class="score-item">B
<div id="score">83+</div>
</div>
<div class="score-item">C
<div id="score">76</div>
</div>
<div class="score-item">D
<div id="score">78</div>
</div>
</div>
This can be MUCH simplified
Note I changed the invalid ID to class="score"
If you cannot do that, then change .querySelector(".score") to .querySelector("div")
document.querySelectorAll('.score-set').forEach(scoreSet => {
const scores = [...scoreSet.querySelectorAll(".score-item")];
scores.sort((a,b) => parseInt(b.querySelector(".score").textContent) - parseInt(a.querySelector(".score").textContent))
scoreSet.innerHTML ="";
scoreSet.append(scores[0])
})
<div class="score-set">
<div class="score-item">A
<div class="score">96+</div>
</div>
<div class="score-item">B
<div class="score">99</div>
</div>
<div class="score-item">C
<div class="score">99</div>
</div>
<div class="score-item">D
<div class="score">96-</div>
</div>
</div>
<div class="score-set">
<div class="score-item">A
<div class="score">86</div>
</div>
<div class="score-item">B
<div class="score">88</div>
</div>
<div class="score-item">C
<div class="score">90</div>
</div>
<div class="score-item">D
<div class="score">90+</div>
</div>
</div>
<div class="score-set">
<div class="score-item">A
<div class="score">83-</div>
</div>
<div class="score-item">B
<div class="score">83+</div>
</div>
<div class="score-item">C
<div class="score">76</div>
</div>
<div class="score-item">D
<div class="score">78</div>
</div>
</div>

how to ADD class in every to div using loop

This is my structure I want to add odd even class in every two divs so how can I achieve this structure using JavaScript loop i tried everything but i got nothing I am learning JavaScript loop so anyone please help me with this
var i = 0;
$('.CollectionInner__Products .Grid__Cell .ProductItem').each(function(i) {
var index = 0;
if (index % 3 == 0) {
$(this).addClass("odd");
}
});
<div class="custompsps">
<div class="ProductItem">
</div>
<div class="ProductItem">
</div>
<div class="ProductItem">
</div>
<div class="ProductItem">
</div>
</div>
<div class="custompsps">
<div class="ProductItem">
</div>
<div class="ProductItem">
</div>
<div class="ProductItem">
</div>
<div class="ProductItem">
</div>
</div>
I want this structure:
i want this stucture
<div class="custompsps">
<div class="ProductItem even">
</div>
<div class="ProductItem even">
</div>
<div class="ProductItem odd">
</div>
<div class="ProductItem odd">
</div>
</div>
<div class="custompsps">
<div class="ProductItem even">
</div>
<div class="ProductItem even">
</div>
<div class="ProductItem odd">
</div>
<div class="ProductItem odd">
</div>
</div>
$('.CollectionInner__Products .Grid__Cell .ProductItem').each(function(index, element) {
$(element).addClass(index & 2 ? "even" : "odd");
});
& is a bitwise "and". index & 2 would be 0 for index 0 and 1, and 2 for index 2 and 3, alternating like this. 0 is falsy and non-0 is truthy. (Your use of "even" and "odd" seem backwards, but I've followed your use.)
jQuery's .each accepts a callback that can take both an index and an element argument.

How do I use JavaScript to select content and move to other elements?

I need to move some text from demoBoxA to demoBoxB.
The demoBoxA parent element has an id selector, but the child element below it has no identifiable selector.
Is it possible to select the text content directly? Then move it into the demoBoxB sub-element (the demoBoxB sub-element has an id selector)
There are 2 difficulties with this issue.
The content of demoBoxA is dynamically generated by the program and the sort is not fixed. There are no identifiable selectors for the subelements.
only need to select part of the content. For example, in the example below, just move the phone model text of "Google", "Huawei", "BlackBerry".
Any help, thanks in advance!
<div class="container" id="demoBoxA">
<div class="row">
<div class="col-md-6">Samsung</div>
<div class="col-md-6">Galaxy S10</div>
</div>
<div class="row">
<div class="col-md-6">Google</div>
<div class="col-md-6">Pixel 4</div>
</div>
<div class="row">
<div class="col-md-6">Sony</div>
<div class="col-md-6">Xperia 5</div>
</div>
<div class="row">
<div class="col-md-6">Huawei</div>
<div class="col-md-6">Mate 30 5G</div>
</div>
<div class="row">
<div class="col-md-6">BlackBerry</div>
<div class="col-md-6">KEY2</div>
</div>
<div class="row">
<div class="col-md-6">Apple</div>
<div class="col-md-6">iPhone 8</div>
</div>
</div>
<div class="container" id="demoBoxB">
<div class="row">
<div class="col-md-6">Google</div>
<div class="col-md-6" id="pixel"></div>
</div>
<div class="row">
<div class="col-md-6">Huawei</div>
<div class="col-md-6" id="mate"></div>
</div>
<div class="row">
<div class="col-md-6">BlackBerry</div>
<div class="col-md-6" id="key2"></div>
</div>
</div>
You can chain selectors like this:
var rows = document.querySelectorAll("#demoBoxA > .row");
That will return a list of all rows inside of demoBoxA. If you need more info about chaining selectors, you can read about it here.
Then, to move the rows you can do this:
var demoBoxB = document.getElementById('demoBoxB');
rows.forEach((row) => {
demoBoxB.appendChild(row);
});
If you just want the text inside each of the columns, you can do this:
var columns = document.querySelectorAll("#demoBoxA > .col-md-6");
var texts = [];
columns.forEach((column) => {
texts.push(column.innerText);
});
Now, texts is an array of the text contents of each column.
If you want to select the cellphone models for each brand, you can do this:
var cols = Array.from(document.querySelectorAll("#demoBoxA > .col-md-6"));
var samsungCol = cols.find((col) => {
return col.textContent == "Samsung";
});
var samsungPhones = [];
samsungCol.parentNode.childNodes.forEach((col) => {
if (col != samsungCol) {
samsungPhones.push(col);
}
});
Now, samsungPhones is a list of columns, one for each Samsung phone (for example).
You can use html drag api .
Just add draggable=true for elements you want to drag and add event listeners for dragstart and dragend
html
<div class="container" id="demoBoxA">
<div class="row " draggable="true">
<div class="col-md-6">Samsung</div>
<div class="col-md-6">Galaxy S10</div>
</div>
<div class="row" draggable="true">
<div class="col-md-6">Google</div>
<div class="col-md-6">Pixel 4</div>
</div>
<div class="row" draggabble="true">
<div class="col-md-6">Sony</div>
<div class="col-md-6">Xperia 5</div>
</div>
</div>
<div class="container " id="demoBoxB">
<div class="row " draggable="true">
<div class="col-md-6">Google</div>
<div class="col-md-6" id="pixel"></div>
</div>
<div class="row" draggable="true">
<div class="col-md-6">Huawei</div>
<div class="col-md-6" id="mate"></div>
</div>
<div class="row" draggable="true">
<div class="col-md-6">BlackBerry</div>
<div class="col-md-6" id="key2"></div>
</div>
</div>
js
document.addEventListener('dragstart', function(e)
{
item = e.target;
}, false);
document.addEventListener('dragend', function(e)
{
document.getElementById("demoBoxB").appendChild(item)
}, false);
Note : you might have to add conditions to check whether the drop is actually happening in demoboxB

Elegant way to select html elements 1,2,5,6 ...with unknown number of elements/

Im looking for elegant way to select only divs on left hand side (marked green).
Number of elements is unknown, so i cannot rely on .eq() or any other function used for filtering elements.
Thank you for any advice.
Html:
<div class="row">
<div class="col-sm-6">
<div>
</div>
</div>
<div class="col-sm-6">
<div>
</div>
</div>
<div class="col-sm-6">
<div>
</div>
</div>
<div class="col-sm-6">
<div>
</div>
</div>
<div class="col-sm-6">
<div>
</div>
</div>
<div class="col-sm-6">
<div>
</div>
</div>
For each div... If its left offset is less than the 3rd one...
It has to be selected.
//Find the offset position of the 3rd div
offset3 = $(".col-sm-6").eq(2).offset().left;
$(".col-sm-6").each(function(){
if($(this).offset().left < offset3){
SelectIt = $(this).children("div"); // Select the "green" inner div
// Do something with SelectIt...
}
});
Try this :
//Select the first 2 elements of each row
var rowSize = 4;
$("div.col-sm-6").filter(function() {
return $(this).index() % rowSize < 2;
});
Demo:
$("div.col-sm-6").filter(function() {
return $(this).index() % 4 < 2
}).addClass('selected');
.selected {
background-color:green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="row">
<div class="col-sm-6">
<div>1
</div>
</div>
<div class="col-sm-6">
<div>2
</div>
</div>
<div class="col-sm-6">
<div>3
</div>
</div>
<div class="col-sm-6">
<div>4
</div>
</div>
<div class="col-sm-6">
<div>5
</div>
</div>
<div class="col-sm-6">
<div>6
</div>
</div>
<div class="col-sm-6">
<div>7
</div>
</div>
<div class="col-sm-6">
<div>8
</div>
</div>
You may use first-child & nth-child.
Hope this snippet will be useful
$("#demoTable tr td:first-child").addClass("myClass");
$("#demoTable tr td:nth-child(2)").addClass("myClass");
JSFIDDLE
Note: I tried with table you can check with div.row

how to append multiple div from different list of length in jquery?

Here, I have two different container. First one is group of anchor links. There is no elements in html. Section one different group of content blocks. How to an append elements based on group of content blocks.
Here is my html,
<div id="group1">
<div class="parent1">
</div>
<div class="parent2">
</div>
<div class="parent3">
</div>
</div>
<div id="group2">
<div class="parentMain1">
<div class="content">test</div>
<div class="content">test</div>
<div class="content">test</div>
</div>
<div class="parentMain2">
<div class="content">test</div>
<div class="content">test</div>
</div>
<div class="parentMain3">
<div class="content">test</div>
</div>
</div>
My result should be,
<div id="group1">
<div class="parent1">
1
2
3
</div>
<div class="parent2">
1
2
</div>
<div class="parent3">
1
</div>
</div>
<div id="group2">
<div class="parentMain1">
<div class="content">test</div>
<div class="content">test</div>
<div class="content">test</div>
</div>
<div class="parentMain2">
<div class="content">test</div>
<div class="content">test</div>
</div>
<div class="parentMain3">
<div class="content">test</div>
</div>
</div>
How to achieve this one with jquery?
You can try this code :
// get all direct child of group 2 element
$('#group2').children().each(function (i, e) {
// get direct child of group 2 element's child
var length = $(this).children().length;
// loop over particular length
for (var m = 1; m <= length; m++) {
// create anchor element
$('<a/>', {
href: '#',
text: m
}).appendTo($('div#group1').find('div:eq(' + i + ')')); // append to group 1 element respectively
}
});
DEMO

Categories