Creating A Custom, Accessible Drop Down

Creating A Custom, Accessible Drop Down

<strong>Creating custom components is hard. You have to override a lot of default browser styling, and often this can be tedious. And in some instances, it's impossible to style the HTML elements. This is the case with the select drop down.</strong>

Creating custom components is hard. You have to override a lot of default browser styling, and often this can be tedious. And in some instances, it's impossible to style the HTML elements. This is the case with the select drop down.

It’s impossible to style the select drop down menu, because we don’t have the ability to wrap the set of <option>elements in a container (which is needed in order to absolutely position the list items against a parent element).

Thus, we must “hack” our way to creating a drop down. Unfortunately, this typically leads to a lack of accessibility.

In this tutorial, we’ll learn how to create a custom select drop down, while abiding by the W3C accessibility standards.

Step 1 - HTML

Here is the drop down we’re going to be creating:

Traditionally, when creating a select drop down, you would use the following:

<select>
    <option value="option-1">Option 1</option>
    <option value="option-2">Option 2</option>
    <option value="option-3">Option 3</option>
</select>


The issue with using the <select> element is that you are unable to wrap the child <option> elements in a container. Why would we need to wrap these elements in a container? In order to position the drop down list underneath the input box.

In our case, we want the list items, <option> elements, to be positioned underneath the <select> box. The browser renders the menu items, by default, as an overlay:

To relatively position a child element in relation to a parent element, such is the case with the custom drop down menu, you must set the following CSS properties:

    .parent {
        position: relative;
    }

    .child {
        position: absolute;
        top: 0;
        left: 0;
     }


You might be wondering: "Can’t you re-write the HTML to the following (using the CSS above)?

    <select class="parent">
        <div class="child">
            <option value="option-1">Option 1</option>
            <option value="option-2">Option 2</option>
            <option value="option-3">Option 3</option>
        </div>
    </select>


The answer is unfortunately no. You cannot place a <div> inside of a <select>.

So we must create an accessible workaround.

Creating A Custom Select

Since we can’t use the <select> element, I’m electing to use a series of <ul> and <li> elements.

The structure looks something like this:

<ul class="dropdown">
  <li class="dropdown__label">
    Label
  </li>

  <!-- The "select" drop down -->
  <li role="button" id="dropdown__selected" tabindex="0">Option 1</li>

  <!-- Icon -->
  <svg class="dropdown__arrow" width="10" height="5" viewBox="0 0 10 5" fill-rule="evenodd">
      <path d="M10 0L5 5 0 0z"></path>
  </svg>

  <li class="dropdown__list-container">
    <ul class="dropdown__list">
      <li class="dropdown__list-item" id="option-1">Option 1</li>
    <li class="dropdown__list-item" id="option-2">Option 2</li>
  </ul>
  </li>
</ul>


This is pretty simple.

  • We have the entire component wrapped in an unordered list.
  • The label is a list item.
  • The select is also a list item.
  • Next we have the drop down arrow icon. And finally, the list item menu is wrapped in a sub-unordered list.

But… this isn’t accessible. If a visually impaired user, with the help of assistive technology, visits this page, they won’t have a clue that this is a drop down or how to interact with it. Additionally, it’s completely inaccessible by keyboard.

Making The Custom Element Accessible

A custom element must function the same as the semantic elements in regards to keyboard navigation and screen reader accessibility.

Here’s what we need in order to make this screen reader accessible:

  • We have the entire component wrapped in an unordered list.
  • The label is a list item.
  • The select is also a list item.
  • Next we have the drop down arrow icon. And finally, the list item menu is wrapped in a sub-unordered list.

Here’s what we need in order to make this keyboard accessible:

  • We have the entire component wrapped in an unordered list.
  • The label is a list item.
  • The select is also a list item.
  • Next we have the drop down arrow icon. And finally, the list item menu is wrapped in a sub-unordered list.

Here’s the accessible HTML:

  <ul class="dropdown">
    <li id="dropdown-label" class="dropdown__label">
      Label
    </li>

    <li
      role="button"
      aria-labelledby="dropdown-label"
      id="dropdown__selected"
      tabindex="0"
    >
      Option 1
    </li>

    <svg
      class="dropdown__arrow"
      width="10"
      height="5"
      viewBox="0 0 10 5"
      fill-rule="evenodd"
    >
      <title>Open drop down</title>
      <path d="M10 0L5 5 0 0z"></path>
    </svg>
    <li aria-expanded="false" role="list" class="dropdown__list-container">
      <ul class="dropdown__list">
        <li class="dropdown__list-item" tabindex="0" id="option-1">
          Option 1
        </li>
        <li class="dropdown__list-item" tabindex="0" id="option-2">
          Option 2
        </li>
      </ul>
    </li>
  </ul>


We also need to add some JavaScript logic to ensure that the component functions the way a native select drop down would. Here is the expected interaction:

  • We have the entire component wrapped in an unordered list.
  • The label is a list item.
  • The select is also a list item.
  • Next we have the drop down arrow icon. And finally, the list item menu is wrapped in a sub-unordered list.

So now let’s implement it.

Implementing Keyboard Accessibility With JavaScript

First, we need to grab the keycodes for the Spacebar, Enter key, up and down arrow keys, and the Escape key. (I’ve seen the Spacebar represented as 0 and 32, so I set it to both to be safe).

  const SPACEBAR_KEY_CODE = 0 || 32;
  const ENTER_KEY_CODE = 13;
  const DOWN_ARROW_KEY_CODE = 40;
  const UP_ARROW_KEY_CODE = 38;
  const ESCAPE_KEY_CODE = 27;


Next, there are a few elements we know we’ll need. I’ll save those to constants. We’ll also want to keep track of the list item ids, so I’ll declare an empty array which we’ll fill up.

  const list = document.querySelector(".dropdown__list");
  const listContainer = document.querySelector(".dropdown__list-container");
  const dropdownArrow = document.querySelector(".dropdown__arrow");
  const listItems = document.querySelectorAll(".dropdown__list-item");
  const dropdownSelectedNode = document.querySelector("#dropdown__selected"); 
  const listItemIds = [];


Next, we need to add some event listeners to our elements to ensure they will respond to user interaction. Don’t worry about the functions declared here, we’ll get to them soon.

  dropdownSelectedNode.addEventListener("click", e =>
    toggleListVisibility(e)
  );
  dropdownSelectedNode.addEventListener("keydown", e =>
    toggleListVisibility(e)
  );

  // Add each list item's id to the listItems array
  listItems.forEach(item => listItemIds.push(item.id));

  listItems.forEach(item => {
    item.addEventListener("click", e => {
      setSelectedListItem(e);
      closeList();
    });

    item.addEventListener("keydown", e => {
      switch (e.keyCode) {
        case ENTER_KEY_CODE:
          setSelectedListItem(e);
          closeList();
          return;

        case DOWN_ARROW_KEY_CODE:
          focusNextListItem(DOWN_ARROW_KEY_CODE);
          return;

        case UP_ARROW_KEY_CODE:
          focusNextListItem(UP_ARROW_KEY_CODE);
          return;

        case ESCAPE_KEY_CODE:
          closeList();
          return;

         default:
           return;
      }
    });
  });


Now let’s create some of these functions we just called in the event listeners.

setSelectedListItem takes an event and updates the currently selected item in the “select” box.

function setSelectedListItem(e) {
  let selectedTextToAppend = document.createTextNode(e.target.innerText);
  dropdownSelectedNode.innerHTML = null;
  dropdownSelectedNode.appendChild(selectedTextToAppend);
}


closeList closes the list and updates the aria-expanded value.

function closeList() {
  list.classList.remove("open");
  dropdownArrow.classList.remove("expanded");
  listContainer.setAttribute("aria-expanded", false);
}


toggleListVisibility takes an event. If the Escape key was pressed, close the list. Otherwise, if the user has clicked or if they’ve pressed the Spacebar or Enter key, toggle the expanded state and update the aria-expandedvalue accordingly. Finally, if the down or up arrow keys were pressed, focus the next list item.

function toggleListVisibility(e) {
  let openDropDown = e.keyCode === SPACEBAR_KEY_CODE || e.keyCode === ENTER_KEY_CODE;

  if (e.keyCode === ESCAPE_KEY_CODE) {
    closeList();
  }

  if (e.type === "click" || openDropDown) {
    list.classList.toggle("open");
    dropdownArrow.classList.toggle("expanded");
    listContainer.setAttribute(
      "aria-expanded",
      list.classList.contains("open")
    );
  }

  if (e.keyCode === DOWN_ARROW_KEY_CODE) {
    focusNextListItem(DOWN_ARROW_KEY_CODE);
  }

  if (e.keyCode === UP_ARROW_KEY_CODE) {
    focusNextListItem(UP_ARROW_KEY_CODE);
  }
}


focusNextListItem takes a direction which is either the const DOWN_ARROW_KEY_PRESSED or UP_ARROW_KEY_PRESSED. If the user is currently focused on the “select”, focus on the first list item. Otherwise we need to find the index of the currently focused list item. This is where the listItemsId array comes in handy. Now that we know where in the list the currently focused item is, we can decide what to do.

If the user pressed the down arrow key, and they’re not at the last list item, focus on the next list item. If the user pressed the up arrow key, and they’re not at the first list item, focus on the previous list item.

function focusNextListItem(direction) {
  const activeElementId = document.activeElement.id;
  if (activeElementId === "dropdown__selected") {
    document.querySelector(`#${listItemIds[0]}`).focus();
  } else {
    const currentActiveElementIndex = listItemIds.indexOf(activeElementId);
    if (direction === DOWN_ARROW_KEY_CODE) {
      const currentActiveElementIsNotLastItem =
      currentActiveElementIndex < listItemIds.length - 1;
      if (currentActiveElementIsNotLastItem) {
        const nextListItemId = listItemIds[currentActiveElementIndex + 1];
        document.querySelector(`#${nextListItemId}`).focus();
      }
    } else if (direction === UP_ARROW_KEY_CODE) {
      const currentActiveElementIsNotFirstItem =
      currentActiveElementIndex > 0;
      if (currentActiveElementIsNotFirstItem) {
        const nextListItemId = listItemIds[currentActiveElementIndex - 1];
        document.querySelector(`#${nextListItemId}`).focus();
      }
    }
  }
}


And that’s it! You now have a fully compliant keyboard-accessible drop down! I won’t be covering the Sass/CSS here, but you’re welcome to check it out on CodePen.

Render HTML with Vanilla JavaScript and lit-html

Render HTML with Vanilla JavaScript and lit-html

Sometimes you need to render HTML elements on a web page. And like Goldilocks' search for "just right", you have to try a few techniques before you find the right one. Using a framework may be too hard. Using pure HTML and the DOM API may be too soft. What you need is something in the middle that is just right. Is lit-html "just right"? Let's find out.

Sometimes you need to render HTML elements on a web page. And like Goldilocks' search for "just right", you have to try a few techniques before you find the right one. Using a framework may be too hard. Using pure HTML and the DOM API may be too soft. What you need is something in the middle that is just right. Is lit-html "just right"? Let's find out.

First, I'll show how this all works. Then at the end of this article, I'll explain everything you need to get started with lit-html to try this for yourself.

When you're done, you can push your HTML app with lit-html to the cloud to see it in all of its glory! I included a link to a free Azure trial, so you can try it yourself.

Resources:

The Sample App

Here is the app I'll demonstrate in this article. It fetches a list of heroes and renders them when you click the button. It also renders a progress indicator while it is fetching.

What's the Value of lit-html

When you focus on rendering content, and nothing else, lit-html is a good fit. It works closely with the DOM to render content, and refresh it in an optimal manner. The docs can provide you with more details, but the basic code for lit-html looks like this.

// Credit: As seen in official docs https://lit-html.polymer-project.org/guide/getting-started

// Import lit-html
import { html, render } from 'lit-html';

// Define a template
const myTemplate = name =>
  html`
    <p>Hello ${name}</p>
  `;

// Render the template to the document
render(myTemplate('World'), document.body);

You import lit-html, define a template, then render it to the DOM. That's it!

Rendering HTML

A progress bar is fairly basic. There is some HTML, and we show it when needed and hide it when it is not required. While we could use a template, or innerHTML, or the DOM API for this, let's see what this would look like with lit-html.

First, we get a reference to the element in the DOM where the progress bar will appear.

Then we define the template. This code looks and feels like JSX (or TSX). The advantage here is that you can write the HTML. You wrap the HTML in a template string (notice the back-tick character is used and not a single quote). Template strings allow you to span lines and insert variables where needed (we'll see this soon). The magic that makes this work is the html tag that precedes the template string. The html tag is what tells lit-html that you are about to define a template.

Next, we compile the template and pass those results to lit-html's render function, which places the results in the DOM. Finally, we hide or show the progress bar as needed.

function showProgress(show = true) {
  const container = document.getElementById('progress-placeholder');

  const template: () => TemplateResult = () => html`
    <progress class="progress is-medium is-info" max="100"></progress>
  `;
  const result = template();
  render(result, container);

  container.style.display = show ? 'block' : 'none';
}

Now you can run this showProgress function any time you want to show the progress bar.

Note that when a template is re-rendered, the only part that is updated is the data that changed. If no data changed, nothing is updated.

Rendering HTML with Dynamic Values

The progress bar does not change each time it is rendered. You will have situations where you want your HTML to change. For example, you may have a message area on your web app that shows a styled message box with a title and a message. The title and message will change every time you show the message area. Now you have dynamic values.

The HTML is defined with a template string, so it is trivial to add a variable into it. Notice the code below adds a title and text into the template, using the ${data.title} and ${data.text} syntax, respectively.

Then the template is compiled and rendered were needed.

When this template is re-rendered, the only part that is updated is the data that changed. In this case, that's the title and text.

function showMessage(text: string, title = 'Info') {
  const template: (data: any) => TemplateResult = (data: Message) => html`
    <div id="message-box" class="message is-info">
      <h3 class="message-header">${data.title}</h3>
      <p class="message-body">${data.text}</p>
    </div>
  `;

  const el = document.getElementById('message-placeholder');
  const result = template({ title, text });
  render(result, el);

  el.style.visibility = !!text ? 'visible' : 'hidden';
}

Rendering a List

Things get a little more real when we render a list. Let's think about that for a moment. A list requires that we have a plan if there is data and a backup plan if there is no data. A list requires that we render the same thing for each row, and we don't know how many rows we have. A list requires that we pass different values for each row, too. Then we have to take the rows and wrap them in a container such as a <ul> or a <table>.

So there is a little more logic here, regardless of whether we use lit-html or any other technique. Let's explore how the replaceHeroList function renders the rows using lit-html.

function replaceHeroList(heroes?: Hero[]) {
 const heroPlaceholder = document.querySelector('.hero-list');

 // Define the template
 let template: () => TemplateResult;

 if (heroes && heroes.length) {
   // Create the template for every hero row
   template = createList();
 } else {
   // Create the template with a simple "not found" message
   template = () =>
     html`
       <p>heroes not found</p>
     `;
 }

 // Compile the template
 const result = template();

 // Render the template
 render(result, heroPlaceholder);

Notice that when there are heroes, we call the createList function. This function begins by creating an array of TemplateResult. So for every hero in the heroes array, we define a template that represents the <li> containing the HTML that displays that respective hero.

Then we create another template that contains the <ul> and embeds the array of hero templates. It's pretty cool that we can embed templates like this! Finally, we return it all and let the logic compile the templates and render them.

function createList() {
  // Create an array of the templates for each hero
  const templates: TemplateResult[] = heroes.map(hero => {
    return html`
      <li>
        <div class="card">
          <div class="card-content">
            <div class="content">
              <div class="name">${hero.name}</div>
              <div class="description">${hero.description}</div>
            </div>
          </div>
        </div>
      </li>
    `;
  });

  // Create a template that includes the hero templates
  const ulTemplate: () => TemplateResult = () =>
    html`
      <ul>
        ${templates}
      </ul>
    `;
  return ulTemplate;
}

Summary

When you want to render HTML, lit-html is a fast and light-weight option. Is it better than using templates and the DOM API? You'll have to decide what is best for you. But the real story here is that you have another great option to consider when determining the right tool for your job.

Prologue

You can also get editor help with your lit-html templates. Notice the image below shows the syntax highlighting for the HTML template!

Setup

You can install the lit-html package with npm.

npm install lit-html

Alternately you can load it directly from the unpkg.com CDN

import { html, render } from 'https://unpkg.com/lit-html?module';

You have a choice here. npm is my preference, but feel 100% free to use the CDN if that suits you.

TypeScript and lit-html

You only need to include the library for lit-html and you're done. But I like to use TypeScript, and I absolutely recommend enabling your tooling to work great with typeScript and lit-html.

Let me be very clear here - you do not need TypeScript. I choose to use it because it helps identify mistakes while I write code. If you don't want TypeScript, you can opt to use plain JavaScript.

Here are the steps to make TypeScript and lit-html light up together:

  1. Install TypeScript support for lit-html
  2. Configure your tsconfig.json file
  3. Install the VS Code extension for lit-html

Run this command to install the plugin and typescript, as development dependencies to your project.

npm install --save-dev typescript-lit-html-plugin typescript

Edit your tsconfig.json by adding the following to your compilerOptions section.

"compilerOptions": {
  "plugins": [
    {
      "name": "typescript-lit-html-plugin"
    }
  ]
}

Finally, install the VS Code extension for lit-html.

Now you get syntax highlighting for all of your lit-html templates!

How to Retrieve full Profile of LinkedIn User using Javascript

How to Retrieve full Profile of LinkedIn User using Javascript

I am trying to retrieve the full profile (especially job history and educational qualifications) of a linkedin user via the Javascript (Fetch LinkedIn Data Using JavaScript)

Here we are fetching LinkedIn data like Username, Email and other fields using JavaScript SDK.

Here we have 2 workarounds.

  1. Configuration of linkedIn developer api
  2. Javascript Code to fetch records

Configuration of linkedIn developer api

In order to fetch records, first we need to create developer api in linkedin which will act as token/identity while fetching data from other linkedin accounts.

So to create api, navigate to https://linkedin.com/developer/apps and click on 'Create Application'.

After navigating, fill in details like name, description and other required fields and then submit.

As we submit, it will create Client ID and Client Secret shown below, which we will be using in our code while communicating to fetch records from other LinkedIn account.

Note: We need to provide localhost Url here under Oauth 2.0. I am using my localhost, but you can probably use other production URLs under Oauth 2.0 where your app is configured. It will make your api  consider the Url as trusted which fetching records.

Javascript Code to fetch records

For getting user details like first name, last name,User image can be written as,

<script type="text/javascript" src="https://platform.linkedin.com/in.js">  
    api_key: XXXXXXX //Client ID  
    onLoad: OnLinkedInFrameworkLoad //Method that will be called on page load  
    authorize: true  
</script>  
<script type="text/javascript">  
    function OnLinkedInFrameworkLoad() {  
        IN.Event.on(IN, "auth", OnLinkedInAuth);  
    }  
  
    function OnLinkedInAuth() {  
        IN.API.Profile("me").result(ShowProfileData);  
    }  
  
    function ShowProfileData(profiles) {  
        var member = profiles.values[0];  
        var id = member.id;  
        var firstName = member.firstName;  
        var lastName = member.lastName;  
        var photo = member.pictureUrl;  
        var headline = member.headline;  
        //use information captured above  
        var stringToBind = "<p>First Name: " + firstName + " <p/><p> Last Name: " + lastName + "<p/><p>User ID: " + id + " and Head Line Provided: " + headline + "<p/>"  
        document.getElementById('profiles').innerHTML = stringToBind;  
    }  
</script>    

Kindly note we need to include 'https://platform.linkedin.com/in.js' as src under script type as it will act on this Javascript SDK provided by Linkedin.

In the same way we can also fetch records of any organization with the companyid as keyword.

<head>  
    <script type="text/javascript" src="https://platform.linkedin.com/in.js">  
        api_key: XXXXXXX ////Client ID  
        onLoad: onLinkedInLoad  
        authorize: true  
    </script>  
</head>  
  
<body>  
    <div id="displayUpdates"></div>  
    <script type="text/javascript">  
        function onLinkedInLoad() {  
            IN.Event.on(IN, "auth", onLinkedInAuth);  
            console.log("On auth");  
        }  
  
        function onLinkedInAuth() {  
            var cpnyID = XXXXX; //the Company ID for which we want updates  
            IN.API.Raw("/companies/" + cpnyID + "/updates?event-type=status-update&start=0&count=10&format=json").result(displayCompanyUpdates);  
            console.log("After auth");  
        }  
  
        function displayCompanyUpdates(result) {  
            var div = document.getElementById("displayUpdates");  
            var el = "<ul>";  
            var resValues = result.values;  
            for (var i in resValues) {  
                var share = resValues[i].updateContent.companyStatusUpdate.share;  
                var isContent = share.content;  
                var isTitled = isContent,  
                    isLinked = isContent,  
                    isDescription = isContent,  
                    isThumbnail = isContent,  
                    isComment = isContent;  
                if (isTitled) {  
                    var title = isContent.title;  
                } else {  
                    var title = "News headline";  
                }  
                var comment = share.comment;  
                if (isLinked) {  
                    var link = isContent.shortenedUrl;  
                } else {  
                    var link = "#";  
                }  
                if (isDescription) {  
                    var description = isContent.description;  
                } else {  
                    var description = "No description";  
                }  
                /* 
                if (isThumbnailz) { 
                var thumbnailUrl = isContent.thumbnailUrl; 
                } else { 
                var thumbnailUrl = "http://placehold.it/60x60"; 
                } 
                */  
                if (share) {  
                    var content = "<a target='_blank' href=" + link + ">" + comment + "</a><br>";  
                    //el += "<li><img src='" + thumbnailUrl + "' alt=''>" + content + "</li>";  
                    el += "<li><div>" + content + "</div></li>";  
                }  
                console.log(share);  
            }  
            el += "</ul>";  
            document.getElementById("displayUpdates").innerHTML = el;  
        }  
    </script>  
</body>  

We can get multiple metadata while fetching records for any any organization. We can get company updates as shown below.

Conclusion

We can also fetch any company specific data like company job updates/post, total likes, comments, and number of views along with a lot of metadata we can fetch which I have shown below.

Thank you for reading !

How to Create an Animated Navbar with Html, CSS and JavaScript

How to Create an Animated Navbar with Html, CSS and JavaScript

In this Html, CSS and JavaScript tutorial you will build an animated navbar with Html, CSS and JavaScript. Have you every wanted to create an awesome animated navbar with JavaScript? We will be looking at making a navbar with Html and CSS and then using the intersection observer in JavaScript to help us create this effect.

Javascript Animated Navbar Tutorial

Have you every wanted to create an awesome animated navbar with javascript? no? oh well...this is akward.

All jokes aside we will be looking at making a navbar with html and css and then using the intersection observer in javascript to help us create this effect.