0

I have DOM elements as shown below. I want to sort it on the basis of href attribute.

This is what I have tried in JS but more need to be done.

document.addEventListener("DOMContentLoaded", function () {
    let elems = Array.from(document.querySelectorAll(".house-senate a"));
    elems.sort((a, b) => a.textContent.localeCompare(b.textContent));

});

Problem Statement:

I am wondering what JS code I need to add so that it sorts everything on the basis of href attributes.

flash
  • 1,455
  • 11
  • 61
  • 132

1 Answers1

4

You're close, but:

  • You need to actually move them in the DOM.
  • You're potentially sorting ones that aren't in the same parent (though they all are in your example HTML).
  • blex pointed out to me that you want to sort by the category in the href, not by the href itself. In your example, it comes to the same thing because the text prior to the category in all the hrefs is the same, but still, perhaps better to extract it.

This is blex's function for extracting it:

function getLinkCategory(a) {
  const matches = a.href.match(/category=([a-z]+)/i);
  return matches ? matches[1] : '';
}

Or if you want to be more rigorous about extracting that parameter from the query string, this collaborative answer originally by Code Spy shows how to do that.

See comments for more:

document.addEventListener("DOMContentLoaded", function () {
    // Get the container
    const container = document.querySelector(".house-senate");
    // Get its immediate child `a` elements
    const elems = [...container.children].filter(child => child.tagName === "A");
    // Sort them
    elems.sort((a, b) => getLinkCategory(a).localeCompare(getLinkCategory(b)));
    // Add them back, which moves them
    for (const el of elems) {
        container.appendChild(el);
    }
});

Live Example:

function getLinkCategory(a) {
  const matches = a.href.match(/category=([a-z]+)/i);
  return matches ? matches[1] : '';
}
document.addEventListener("DOMContentLoaded", function () {
    // Get the container
    const container = document.querySelector(".house-senate");
    // Get its immediate child `a` elements
    const elems = [...container.children].filter(child => child.tagName === "A");
    // Sort them
    elems.sort((a, b) => getLinkCategory(a).localeCompare(getLinkCategory(b)));
    // Add them back, which moves them
    for (const el of elems) {
        container.appendChild(el);
    }
});
<div class="house-senate widget widget-cpac-depth -horizontal">
    <h1 class="widget__title">Committees</h1>
    <a href="/en/?s=&amp;category=BOIE">
        <div class="committee">
            <div class="color-green">BOIE</div>
            <p>Board of Internal Economy</p>
        </div>
    </a>
    <a href="/en/?s=&amp;category=CACN">
        <div class="committee">
            <div class="color-green">CACN</div>
            <p>Canada-China Relations</p>
        </div>
    </a>
    <a href="/en/?s=&amp;category=CHPC">
        <div class="committee">
            <div class="color-green">CHPC</div>
            <p>Canadian Heritage</p>
        </div>
    </a>
    <a href="/en/?s=&amp;category=CIIT">
        <div class="committee">
            <div class="color-green">CIIT</div>
            <p>International Trade</p>
        </div>
    </a>
</div>

If you need to add more sorting criteria (per your comment under the question), just add them in the sort callback; this question has answers showing how to sort an array of objects on multiple criteria.

I've assumed above that there aren't hundreds of these links. If there are, and if you see a performance problem with the above, you can remove the container from the DOM before moving the links around within it, then put it back:

Live Example:

function getLinkCategory(a) {
  const matches = a.href.match(/category=([a-z]+)/i);
  return matches ? matches[1] : '';
}
document.addEventListener("DOMContentLoaded", function () {
    // Get the container
    const container = document.querySelector(".house-senate");
    // Remember its parent and following sibling and remove it
    const parent = container.parentNode;
    const sibling = container.nextSibling;
    parent.removeChild(container);
    // Get its immediate child `a` elements
    const elems = [...container.children].filter(child => child.tagName === "A");
    // Sort them
    elems.sort((a, b) => getLinkCategory(a).localeCompare(getLinkCategory(b)));
    // Add them back, which moves them
    for (const el of elems) {
        container.appendChild(el);
    }
    // Put the container back -- note this works even if the
    // container was the last child in the parent
    // and `sibling` is `null`.
    parent.insertBefore(container, sibling);
});
<div>This is before the <code>div</code> with the links in it.</div>
<div class="house-senate widget widget-cpac-depth -horizontal">
    <h1 class="widget__title">Committees</h1>
    <a href="/en/?s=&amp;category=BOIE">
        <div class="committee">
            <div class="color-green">BOIE</div>
            <p>Board of Internal Economy</p>
        </div>
    </a>
    <a href="/en/?s=&amp;category=CACN">
        <div class="committee">
            <div class="color-green">CACN</div>
            <p>Canada-China Relations</p>
        </div>
    </a>
    <a href="/en/?s=&amp;category=CHPC">
        <div class="committee">
            <div class="color-green">CHPC</div>
            <p>Canadian Heritage</p>
        </div>
    </a>
    <a href="/en/?s=&amp;category=CIIT">
        <div class="committee">
            <div class="color-green">CIIT</div>
            <p>International Trade</p>
        </div>
    </a>
</div>
<div>This is after the <code>div</code> with the links in it.</div>

Note: You're using modern language features, but the above relies on a modern browser feature (NodeList being iterable). If you're transpiling, it may not be that all the browsers you're targeting have the necessary feature, but for anything even vaguely modern, you can polyfill it; see my answer here for details.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • He needs to sort them based on the caregory in the `href` attribute. See my _(deleted)_ answer – blex Feb 28 '20 at 16:28
  • 1
    @blex - Ah, thanks, I misread the comment about that! Although actually, since the text prior to `category` is the same in all the `href`s, sorting by `href` does sort by `category`... :-) – T.J. Crowder Feb 28 '20 at 16:32
  • 1
    Indeed it does :) Note: this extracting function is very basic and will only extract letters. A more complete way of doing it would be [this answer by Code Spy](https://stackoverflow.com/a/901144/1913729) – blex Feb 28 '20 at 16:44