Reputation: 385
I am trying to learn Javascript alone so please don't suggest a library or jQuery.
I have a list of divs and I want the user to be able to sort them by their value. For example:
<button onclick="sort();">Test</button>
<div class="num">2</div>
<div class="num">3</div>
<div class="num">8</div>
<div class="num">1</div>
JS:
function sort(){
var elements = document.getElementsByClassName("num");
elements.sort();
}
I cannot find a straight answer to what's wrong with this. Does getElementsByClassName
return an array of the values of each div with that name? How, when the array is then sorted, so I reflect the changes in the divs?
Upvotes: 5
Views: 3569
Reputation: 79032
You can't use the sort()
function on a NodeList, which is what you are actually getting by calling getElementsByClassName
or querySelectorAll
.
So you'll have to convert it to an array before using Array.sort()
:
// Get elements and convert to array
const elems = [...document.querySelectorAll(".num")];
// Sort elements in-place
elems.sort((a, b) => Number(a.innerText) - Number(b.innerText));
// Join the array back into HTML
const outputHtml = elems.reduce((a, el) => a + el.outerHTML, "");
// Append HTML to parent container
document.getElementById('myDiv').innerHTML = outputHtml;
Upvotes: 6
Reputation: 44609
The problem is that getElementsByClassName
return a nodeList
, not an array. Ok, I have to say, that is very stupid and I still can't get my head around why browser implemented it this way...
What you can do though, is first convert the nodeList
to an array and than do the sorting:
var elems = Array.prototype.slice.call(elements);
elems.sort(/* function (a, b) {} */);
Note that the sorting function is optionnal and is passed normally after the first value. (Although, called directly on your elements list, .sort()
won't sort anything without a function parameter as it won't know which one will come before the other)
Checkout MDN call
description to understand how this really works behind the scene: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
Upvotes: 0
Reputation: 147403
Sorting nodes is not that simple, you may need to deal with other nodes that might get in the way. Anyhow, here's a function that accepts a NodeList or HTMLCollection, converts it to an array, sorts it, then puts the nodes back in order. Note that the elements are put back at the bottom of the parent element, so any other elements will end up at the top. Anyhow, it demonstrates an approach that you can adapt.
There are many other approaches, including others using DOM methods, beware of any that are based on munging the markup. Also, beware of cloning elements as this may have unwelcome side effects on listeners (they may or may not be removed, depending on the browser and how they've been added).
<script type="text/javascript">
function getText(el) {
return el.textContent || el.innerText || '';
}
function sortElements(nodeList) {
// Assume there's a common parent
var node, parentNode = nodeList[0].parentNode
// Define a sort function
function sortEls(a, b) {
var aText = getText(a);
var bText = getText(b);
return aText == bText? 0 : aText < bText? -1 : 1;
}
// Convert nodelist to an array and remove from the DOM at the same time
var a = [], i = nodeList.length;
while (i--) {
a[i] = parentNode.removeChild(nodeList[i]);
}
// Sort the array
a.sort(sortEls);
// Put elements back in order
i = 0;
while (node = a[i++]) {
parentNode.appendChild(node);
}
}
</script>
<div>0</div>
<div>4</div>
<div>2</div>
<div>3</div>
<div>5</div>
<button onclick="sortElements(document.getElementsByTagName('div'))">Sort</button>
Upvotes: 0