Where to Discuss?

Local Group

Preface

Goal: Adding javascript interactivity. Exploring DOM.

This is basically just, a tabs component with javascript interactivity.

Examples

For tutorial purpose we need three layouts:

  1. Simple layout, to example of oldschool javascript.

  2. Simple layout, to explain unobtrusive javascript.

  3. Enhance layout, as real live example.

This article cover the last two parts only. As we have already discuss about the first part in the previous article.


Simple Tabs Unobtrusive: Structure

Can you see?

Inline javascript is annoying. We can add as many event as we want. After a while the HTML looks mess. We need to find a way to make the structure looks tidy.

Document: HTML Head

We can add the javascript at the beginning. We still can reuse previous stylesheet.

  <link rel="stylesheet" type="text/css"
        href="../css/02-simple-layout.css">
  <script src="../js/custom/unobtrusive-simple.js"></script>

Document: HTML Body: Tab Headers

No inline javascript now. The javascript is unobtrusive.

    <div class="tab-headers">
      <div data-target="home"  data-color="bg-blue-500">Home</div>
      <div data-target="team"  data-color="bg-teal-500"
           class="active">Team</div>
      <div data-target="news"  data-color="bg-red-500">News</div>
      <div data-target="about" data-color="bg-orange-500">About</div>
    </div>

Document: HTML Body: Tab Content

Now we have a chance to explore more elements. Using four items, each with their own ids.

    <div class="tab-contents">
      <div id="home">
        <h3>Home</h3>
        <p>Lorem ipsum dolor sit amet,
           consectetur adipiscing elit.
           Quisque in faucibus magna.</p>
      </div>
      <div id="team">
        <h3>Team</h3>
        <p>Nulla luctus nisl in venenatis vestibulum.
           Nam consectetur blandit consectetur.</p>
      </div>
      <div id="news">
        <h3>News</h3>
        <p>Phasellus vel tempus mauris,
           quis mattis leo.
           Mauris quis velit enim.</p>
      </div>
      <div id="about">
        <h3>About</h3>
        <p>Interdum et malesuada fames ac ante
           ipsum primis in faucibus.
           Nulla vulputate tortor turpis,
           at eleifend eros bibendum quis.</p>
      </div>
    </div>

Simple Tabs Unobtrusive: Javascript

This is basically the same with the previous one. With some additional stuff.

Javascript: DOM Event

How can I put the the external javascript between <head>...</head>, instead of put it below?

This simply by wrapping the codes, inside an event called DOMContentLoaded.

document.addEventListener('DOMContentLoaded', function(event) { 

  // code here …

});

Javascript: Managing Contents

By selecting the id of the content, we can toggle the visibility.

  Array.from(tabHeaders.children).forEach((targetHeader) => {
    // Tab Headers: All Click Events
    targetHeader.addEventListener('click', () => {
      const targetName = targetHeader.dataset.target;
      const colorClass = targetHeader.dataset.color;
      const targetContent = document.getElementById(targetName);

      // …

      // Showing the content
      Array.from(tabContents.children).forEach((tabContent) => {
        tabContent.style.display = 'none';
      });
      targetContent.style.display = 'block';
      targetContent.classList.add(colorClass);
    });
  });

Javascript: Fat Arrow

You may notice the fat arrow here:

  Array.from(tabHeaders.children).forEach((targetHeader) => {
    //  ..
  });

It is a shortcut to previously written function:

  // Set all to default setting
  Array.from(tabHeaders.children)
    .forEach(function(tabHeader) {
      // ..
    });

Javascript: Complete

The complete javascript is here below:

document.addEventListener('DOMContentLoaded', function(event) { 
  const tabHeaders  = document.getElementsByClassName('tab-headers')[0];
  const tabContents = document.getElementsByClassName('tab-contents')[0];
      
  Array.from(tabHeaders.children).forEach((targetHeader) => {
    // Tab Headers: All Click Events
    targetHeader.addEventListener('click', () => {
      const targetName = targetHeader.dataset.target;
      const colorClass = targetHeader.dataset.color;
      const targetContent = document.getElementById(targetName);

      // Set all to default setting
      Array.from(tabHeaders.children).forEach((tabHeader) => {
        tabHeader.classList.remove('active');
        tabHeader.classList.remove(tabHeader.dataset.color);
        tabHeader.classList.add('bg-gray-700');
      });
      // Except the chosen one
      targetHeader.classList.add('active');
      targetHeader.classList.remove('bg-gray-700');
      targetHeader.classList.add(colorClass);

      // Showing the content
      Array.from(tabContents.children).forEach((tabContent) => {
        tabContent.style.display = 'none';
      });
      targetContent.style.display = 'block';
      targetContent.classList.add(colorClass);
    });
  });

  // Tab Headers: Default
  tabHeaders.getElementsByClassName('active')[0].click();
});

Preview

Now we have proper color for the right content.

Tabs Component: Simple Layout: Unobtrusive

Each time you click the left tab, the color on the right will be changed.


Enhanced Tabs Unobtrusive: Structure

How about real live component?

Document: HTML Head

We can add the javascript at the beginning. We still can reuse previous stylesheet.

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tabs - Unobtrusive Javascript</title>
  <link rel="stylesheet" type="text/css" href="../css/02-enhanced-layout.css">
  <link rel="stylesheet" type="text/css" href="../css/03-background-colors.css">
  <link rel="stylesheet" type="text/css" href="../css/02-border-radius.css">
  <script src="../js/custom/unobtrusive-enhanced.js"></script>

Document: HTML Body: Tab Headers

We have the third level div.

    <div class="tab-headers">
      <div data-target="home"  data-color="bg-blue-500">  <div>Home</div></div>
      <div data-target="team"  data-color="bg-teal-500" class="active">
        <div>Team</div></div>
      <div data-target="news"  data-color="bg-red-500">   <div>News</div></div>
      <div data-target="about" data-color="bg-orange-500"><div>About</div></div>
    </div>
    
    <div class="tab-spacer"></div>

With additional tab-spacer.

Document: HTML Body: Tab Content

We also have the third level div here.

    <div class="tab-contents">
      <div id="home"><div class="tab-content">
        <h3>Home</h3>
        <p>Lorem ipsum dolor sit amet,
           consectetur adipiscing elit.
           Quisque in faucibus magna.</p>
      </div></div>
      <div id="team"><div class="tab-content">
        <h3>Team</h3>
        <p>Nulla luctus nisl in venenatis vestibulum.
           Nam consectetur blandit consectetur.</p>
      </div></div>
      <div id="news"><div class="tab-content">
        <h3>News</h3>
        <p>Phasellus vel tempus mauris,
           quis mattis leo.
           Mauris quis velit enim.</p>
      </div></div>
      <div id="about"><div class="tab-content">
        <h3>About</h3>
        <p>Interdum et malesuada fames ac ante
           ipsum primis in faucibus.
           Nulla vulputate tortor turpis,
           at eleifend eros bibendum quis.</p>
      </div></div>
    </div>

Enhanced Tabs Unobtrusive: Javascript

This is also an enhancement of the previous script. With some additional event, to mimic the mouse hover.

Javascript: Managing Headers

Managing third level div can be done adding bg-white class.

     // Set all to default setting
      Array.from(tabHeaders.children).forEach((tabHeader) => {
        // …
        tabHeader.firstElementChild.classList.remove('bg-white');
      });
      // Except the chosen one
      // …
      targetHeader.firstElementChild.classList.add('bg-white');

Javascript: Managing Contents

It is exactly the same with the simple tabs layout. No code is changed here.

Preview

Tabs Component: Enhanced Layout: Unobtrusive

Javascript: Mimic Hover

We can utilize this event:

  • onmouseenter: add class is-hovered, and

  • onmouseleave: remove class is-hovered.

Both can be implemented as below:

    // Tab Headers: Mimic All Hover Events
    targetHeader.addEventListener('mouseenter', () => {
      const targetName = targetHeader.dataset.target;
      const targetContent = document.getElementById(targetName);
      targetContent.firstElementChild.classList.add('is-hovered');
    });

    targetHeader.addEventListener('mouseleave', () => {
      const targetName = targetHeader.dataset.target;
      const targetContent = document.getElementById(targetName);
      targetContent.firstElementChild.classList.remove('is-hovered');
    });

While the stylesheet has been previously written as:

.tab-headers div.active div:hover {
  background-color: #e0e0e0;
}

.tab-contents div div.is-hovered {
  background-color: #e0e0e0;
}

Preview

Now whenever the mouse hovering the active tabs, we can see something as below:

Tabs Component: Enhanced Layout: Hovered

Javascript: Complete Code

Too long?

Plain javascript is still an option, if you do not want to use framework. It safe, supported in most browser, but not cool for kids these days.

As a conclusion, we can summarized the complete code as below.

document.addEventListener('DOMContentLoaded', function(event) { 
  const tabHeaders  = document.getElementsByClassName('tab-headers')[0];
  const tabContents = document.getElementsByClassName('tab-contents')[0];
      
  Array.from(tabHeaders.children).forEach((targetHeader) => {
    // Tab Headers: All Click Events
    targetHeader.addEventListener('click', () => {
      const targetName = targetHeader.dataset.target;
      const colorClass = targetHeader.dataset.color;
      const targetContent = document.getElementById(targetName);

      // Set all to default setting
      Array.from(tabHeaders.children).forEach((tabHeader) => {
        tabHeader.classList.remove('active');
        tabHeader.classList.remove(tabHeader.dataset.color);
        tabHeader.classList.add('bg-gray-700');
        tabHeader.firstElementChild.classList.remove('bg-white');
      });
      // Except the chosen one
      targetHeader.classList.add('active');
      targetHeader.classList.remove('bg-gray-700');
      targetHeader.classList.add(colorClass);
      targetHeader.firstElementChild.classList.add('bg-white');

      // Showing the content
      Array.from(tabContents.children).forEach((tabContent) => {
        tabContent.style.display = 'none';
      });
      targetContent.style.display = 'block';
      targetContent.classList.add(colorClass);
    });

    // Tab Headers: Mimic All Hover Events
    targetHeader.addEventListener('mouseenter', () => {
      const targetName = targetHeader.dataset.target;
      const targetContent = document.getElementById(targetName);
      targetContent.firstElementChild.classList.add('is-hovered');
    });

    targetHeader.addEventListener('mouseleave', () => {
      const targetName = targetHeader.dataset.target;
      const targetContent = document.getElementById(targetName);
      targetContent.firstElementChild.classList.remove('is-hovered');
    });
  });

  // Tab Headers: Default
  tabHeaders.getElementsByClassName('active')[0].click();
});

You can see how the length of the javascripts hogging your screen. This is an issue. We should find a way to make this easier to be read.


What’s Next?

Yeah we will find a way, but first, let’s port the stylesheet to Tailwind CSS.

Consider continue reading [ Tabs - NPM - Tailwind CSS ].