0

I have and alphabetical list -

<ul id="my-list">
  <li>Afghanistan</li>
  <li>Albania</li>
  <li>Algeria</li>
  <li>Belgium</li>
  <li>Belize</li>
  <li>Brazil</li>
  <li>Canada</li>
  <li>Chile</li>
  <li>Columbia</li>
  ...
</ul>

What I need to do is dynamically add letter dividers where each new letter starts -

<ul id="my-list">
  <div>A</div>
  <li>Afghanistan</li>
  <li>Albania</li>
  <li>Algeria</li>
  <div>B</div>
  <li>Belgium</li>
  <li>Belize</li>
  <li>Brazil</li>
  <div>C</div>
  <li>Canada</li>
  <li>Chile</li>
  <li>Columbia</li>
  <div>D</div>
  ...
</ul>

Is this possible?

  • 2
    Not though that your HTML is *invalid*. `ul` cannot have divs as children. – Paulie_D Oct 08 '18 at 16:10
  • 5
    *to answer your question:* yes this is definetly possible. – Jonas Wilms Oct 08 '18 at 16:12
  • 1
    Possible, only thing left is to start thinking and coding. – Void Spirit Oct 08 '18 at 16:17
  • For structure advice, this might be informative: [How to semantically add heading to a list](https://stackoverflow.com/questions/8935262/how-to-semantically-add-heading-to-a-list). – showdev Oct 08 '18 at 16:40
  • Thanks all for your comments. I had been looking into this myself but every avenue I took led me to a dead end. A combination of my mind being turned to jelly and the mismash of methods I used made me decide to write the problem out as simply as possible. Thanks again everyone fr your help :) – Jonathan Hutchinson Oct 08 '18 at 16:56

3 Answers3

3

Sure, it's possible. You are of course meant to look these things up yourself, but here's a possible approach.

const list = Array.from(document.querySelector('#my-list').children);
list.forEach(i => {
 const letter = i.innerHTML.charAt(0);
 if (!accountedFor(letter)) i.insertAdjacentHTML('beforebegin', `<div>${letter}</div>`); 
});

function accountedFor(letter) {
 return document.querySelector('#my-list').innerHTML.includes(`<div>${letter}</div>`);
}
<ul id="my-list">
 <li>Afghanistan</li>
 <li>Albania</li>
 <li>Algeria</li>
 <li>Belgium</li>
 <li>Belize</li>
 <li>Brazil</li>
 <li>Canada</li>
 <li>Chile</li>
 <li>Columbia</li>
</ul>
Darren
  • 710
  • 1
  • 6
  • 11
1

If you could slightly change the markup by inserting a data- attribute that contains the same country name e.g.

<ul id="countries">
  <li data-country="Afghanistan">Afghanistan</li>
  <li data-country="Albania">Albania</li>
  <li data-country="Algeria">Algeria</li>
  <li data-country="Belgium">Belgium</li>
  <li data-country="Belize">Belize</li>
  <li data-country="Brazil">Brazil</li>
  <li data-country="Canada">Canada</li>
  <li data-country="Chile">Chile</li>
</ul>

you could just use CSS like this

li:first-child:before,
[data-country^="A"] + :not([data-country^="A"]):before,
[data-country^="B"] + :not([data-country^="B"]):before,
...,
[data-country^="Y"] + :not([data-country^="Y"]):before {
  content: attr(data-country);
  display: block;
  font-size: inherit;
  margin-left: -1em;
  overflow: hidden;
  width: 2em;
  letter-spacing: 100vw;
}

Basically with this solution you write the first data-country attribute for each letter, but you just show the first letter of the country name thanks to a huge letter-spacing and a hidden overflow.


Demo

Note that if you can't manually alter the markup then you could still use this approach by adding dynamically that data-country attribute via JS like this

let countries = document.querySelectorAll('#countries li');
[...countries].forEach((country) => {
  country.dataset.country = country.textContent;
});

No invalid or extra markup and just a bit of javascript in the worst case

Fabrizio Calderan
  • 120,726
  • 26
  • 164
  • 177
0

I've made a simple solution in JQuery. This code will dynamically add the first letter.
See this : https://codepen.io/nisaifudeen/pen/PyWYjQ

JQuery

//Taking Full <li>
var item = $('#my-list').find('li');
var currentLetter = "";


// Each item
item.each(function(e, item){
  //e is the index
  // Item is the element
  var firstLetter = $(item).html().charAt(0);

  //Condition for not to add the header multiple times
  if(currentLetter !== firstLetter){
    $('<li class="header-set"><p>' + firstLetter + '</p> 
   </li>').insertBefore($(item));    
  }

  currentLetter = firstLetter;

});


You can use .each() function.

saifudeen ni
  • 145
  • 9