Home > OS >  Sort HTML Elements with Javascript to match specific pattern
Sort HTML Elements with Javascript to match specific pattern

Time:12-06

I am trying to sort an array of HTML Elements based on their classes to fit a certain pattern but i am not sure how to solve this.

Context: There are 2 specific types of news on a website: normal and featured. Normal news have 25% of width and featured news 50%. Because their are sorted by date it can happen, that a row has a 25% of empty space. I am trying to fix this.

// simplified const expected = ['a','a','b','a','b','a','b','a','a', ...rest]
const expectedArray = ['.news-normal', '.news-normal', '.news-featured', '.news-normal', 'news-featured','.news-normal', '.news-featured', '.news-normal', '.news-normal', ...rest]

I was thinking about getting all the elements with querySelectorAll and comparing them with the expected array via index, splice it and put it the end of the array if not the same but i am not sure if this is the right way

Any tips / best practices to solve this kind of issue? Thanks

// const allNews = document.querySelectorAll('.news')
const allNews = ['.news-featured', '.news-featured', '.news-normal', '.news-normal', 'news-featured','.news-normal', '.news-featured', '.news-featured', '.news-featured', '.news-normal' ]
const expectedArray = ['.news-normal', '.news-normal', '.news-featured', '.news-normal', 'news-featured','.news-normal', '.news-featured', '.news-normal', '.news-normal',]

allNews.forEach((element, index) => {
  if (element !== expectedArray[index]) {
    allNews.splice(index, 1)
    allNews.push(element)
  }
})

console.log(allNews)

// Output -> ❌
// allNews = ['.news-featured', '.news-normal', 'news-featured', '.news-normal', '.news-featured', '.news-normal', '.news-featured', '.news-normal', '.news-featured', '.news-featured']


Codepen

CodePudding user response:

There's several ways how you can approach this, but what I would do is break down the existing feed, and build up a new feed from scratch. I.e. something like this:

  1. Split allNews into 2 separate lists: featuredNews and normalNews
const featuredNews = allNews.filter(item => isFeatured(item));
const normalNews = allNews.filter(item => !isFeatured(item));
  1. Rebuild the feed based on your pattern:
const sorted = expectedArray.map(type => {
  if (type === '.news-featured') {
    return featuredNews.shift();
  } else {
    return normalNews.shift();
  }
});

Now this solution assumes that expectedArray fully covers allNews (same number of total items, featured items, and normal items). If that's not the case, then you'll need some additional work, but this should give you a good starting point.


Aside from answering your question: in your initial code snippet you're manipulating an array while iterating over it (i.e. using .splice and .push within .foreach). This is generally not a good idea, as it gives some rather unexpected behaviour. See also this question.

CodePudding user response:

If you want to keep the original order as much as possible in tact, then you could consider to move a normal-news item to the next row when there is an "overflow" of a featured-news item. This doesn't produce the order you had in mind, but it does make the items fit the 100% width.

Here is an implementation of that idea.

const allNews = document.querySelectorAll('.news');
const button = document.getElementById("sort");

button.addEventListener('click', () => {
  let sumWidth = 0;
  let lastNormal = null;
  allNews.forEach((element) => {
    let width = 1   element.classList.contains("news-featured");
    sumWidth  = width;
    if (sumWidth > 4) { // Does not fit. Move small element to next row
        element.parentNode.insertBefore(lastNormal, element.nextElementSibling);
    }
    if (width == 1) lastNormal = element;
    sumWidth %= 4;
  });
});
.col-lg-6 { width: 50%; }
.col-lg-3 { width: 25%; }
h1 { font-size: 20px }
.news {
  box-sizing: border-box;
  display: inline-block;
  float: left;
  border: 1px solid;
  height: 50px
}

.news-normal {
  background: lightgrey;
}

.news-featured {
  background: skyblue;
}
<div >
  <div >

    <div >
      <h1>Featured News</h1>
    </div>
    <div >
      <h1>Featured News</h1>
    </div>
    <div >
      <h1>Normal News</h1>
    </div>
    <div >
      <h1>Normal News</h1>
    </div>
    <div >
      <h1>Normal News</h1>
    </div>
    <div >
      <h1>Featured News</h1>
    </div>
    <div >
      <h1>Normal News</h1>
    </div>
    <div >
      <h1>Normal News</h1>
    </div>
    <div >
      <h1>Normal News</h1>
    </div>
    <div >
      <h1>Featured News</h1>
    </div>
    <div >
      <h1>Normal News</h1>
    </div>
    <div >
      <h1>Featured News</h1>
    </div>
  </div>
</div>

<div >
  <div >
    <div >
      <button id="sort">Let's sort</button>
    </div>
  </div>
</div>

  • Related