Skip to content

Instantly share code, notes, and snippets.

@felipenmoura
Last active February 17, 2018 18:25
Show Gist options
  • Save felipenmoura/e99714c5f292c0ea957fea468656cfba to your computer and use it in GitHub Desktop.
Save felipenmoura/e99714c5f292c0ea957fea468656cfba to your computer and use it in GitHub Desktop.
Proposal for an API to deal with HTML Ordered/Unordered List Decorators.

HTML List Item Decorators

About the proposal

I always wanted to be able to access and customize the bullets or numbers in HTML lists.

For example...in the following list:

<ol type="1" start="42" reversed>
  <li>AAA</li>
  <li>BBB</li>
  <li>CCC</li>
  <li>DDD</li>
  <li>EEE</li>
  <li>FFF</li>
</ol>

This proposal will also work well when appending, prepending or replacing childs in lists.

The problem

Accessing the number

The user can see the number in the list, but if I want to know the number of the item the user has clicked, for example, I have to count their previous siblings. And have to think about reverse and start as well. And all that ignoring other things like "list-style-position", "list-style-type" and "list-style-image", which would apply different rules using css, instead of HTML attributes.

Decorating bullets/numbers

If I want a specific bullet or number to be in a different color or type, I can't.

The proposed solution

Decorators

I believe we could have access to something like decorators in both HTMLOListElement and HTMLUListElement.
By accessing someListElement.decorator we would have access to a HTMLListIemDecorator instance.

The developer will also be able to extend the decorator class used in a list, by setting the attribute implements in the list, pointing a class in the current scope.

Properties:

Method Description
type (readonly) the list type, like number, circle, etc
toString the value, like "C" or "34", or the utf8 symbol for Unordered lists
value the value itself, like "C" or 34 (of type integer), or the utf8 symbol for Unordered lists
index the real index number of this item
alt a readable value for screen readers
separator the separator between the decorator and the list item content
style a CSS style for that prticular bullet/number
item a reference to the list item this decorator is being applied to
list a reference to the parent list element for this item

This way, developers would be able to customize that particular bullet or number, as well as read the current number, for example.

    const dec = someOLItem.decorator // the decorator instance
    console.log(someOLItem.decorator) // 33, for example
    console.log(someULItem.decorator) // utf8 code for circle, for example

If the decorator is an image, we would see its path in the value (if image loaded successfuly).

Methods

Method Description Return
isLast True if current listItem is the last in its list Boolean
isFirst True if current listItem is the first in its list Boolean
isFallback True if it was of type image, but failed loading the image false of the path for the image that failed
getPrevious Gets the previous decorator, or null if it is the first HTMLListIemDecorator
getNext Gets the next decorator, or null if this is the last one HTMLListIemDecorator

Enters the HTMLListIemDecorator class

We could have a HTMLListIemDecorator class that could be extended and implemented like so:

class myListItemDecorator extends HTMLListIemDecorator {
  get () {
    return this.value
  }
}

And then (that's the part I'm really not comfortable with, yet)

<ol type="1" start="42" reversed implements="myListItemDecorator">
    ...
</ol>

That shall allow us to do something like this, for an useful example:

class qAndA extends HTMLListIemDecorator {
  get () {
    if (this.index % 2 === 0) {
      // even
      this.value = "A"
      this.separator = ': '
      this.alt = 'Answer'
    } else {
      // odd
      const questionNumber = Math.ceil(this.index / 2)
      this.alt = "Question " + questionNumber
      this.value = "Q" + questionNumber
      this.separator = ') '
      this.style.fontWeight = 'bold'
    }
    return this
  }
}

Then, we could use it like this:

<ol type="1" implements="qAndA">
    <li>Is the Earth flat?</li>
    <li>Nope, it isn't!</li>
    <li>Why is six affraid of seven?</li>
    <li>Because seven eight nine</li>
    <li>Why did the chicken cross the street?</li>
    <li>Because it was bored</li>
</ol>

And see as the result:

Q1) Is the Earth flat?
A: Nope, it isn't!
Q2) Why is six affraid of seven?
A: Because seven eight nine
Q3) Why did the chicken cross the street?
A: Because it was bored

And in that example, the questions should be bold

Styling

Styling wouldn't change a bit, as we already have some basic style properties to work with, like "list-style-position", "list-style-type" and "list-style-image".

The difference is that now, we can customize it decorators one by one or, use a pseudo-element for that.
The decorator pseudo element:

.someList:decorator {
    color: red;
}

.someList li:nth-child(3):decorator {
    color: blue;
}

.someList li:first-child:decorator:before {
    content: "Follow these steps";
}

Accessibility

The accessibility should be enhanced by using a alt in the decorators.
For instance, a screen reader could read "Question 1" instead of "1" or "Q1" in the qAndA example above.

I18N

Internationalization can also be accomplished by reading the lang attribute in the list element or the page and using it to determine which label should be used in the decorator.

Note that this could even allow you to use Date Objects as your list decorators, assuming you customized them in your class extension.

Performance

Although it looks like an HTML element, it will not repaint or redraw in different moments.

For example, changing the innerHTML or innerText of a decorator should not work as it is merely a decorator for its list item.

In order to redraw a decorator, something that reflects into it should be changed in the list item it's related to.

For example, if you have the following implementation:

class todoList extends HTMLListIemDecorator {
  get () {
    if (this.item.classList.has('done')) {
      // even
      this.style.color = '#f0f0f0'
    }
    return this
  }
}

You could update the decorator style by adding or removing the class "done" in your list items:

<ol type="1" implements="todoList">
    <li class="done">Buy bread</li>
    <li>Buy milk</li>
    <li class="done">Aniversary</li>
    <li>Pay bills</li>
    <li>Send some important e-mail</li>
    <li>Call mom</li>
</ol>
listItem.addEventListener('click', event => {
    this.classList.toggle('done')
})

Custom Elements

You can create your own Custom Elements to implement the your customized class for your decorators.
This allows you to have a seprate scope protecting your variables and keeping your code more organized.

Discussion

Feel free to leave your comments in the GIST of this proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment