Where to Discuss?

Local Group

Preface

Goal: Toggle layout apperance with plain javascript.

This is more like a javacript tutorial, which use a bunch of Bulma class.


1: General Preview

This below is general preview of, what javascript toggler page design that we want to achieve.

Bulma SASS: General Preview of Javascript TogglerDesign

Source image is available in inkscape SVG format, so you can modify, and make your own preview quickly.


2: Toggler Component

Sidebar design is siginficantly cool for short article, but for long article this would make your appearance looks unbalanced. Sidebar is necessary, but it takes space on your screen. Talking about space real estate, sometimes we need to be more flexible, while reading an article in a blog. This means we can let user to decide how our layout arranged, by in the same time we provide default layout.

Instead of changing the whole design layout alltogether, this article show how we can achieve this without too much effort.

Two Buttons

Our toggler component consist of two buttons:

  • Left Button: Release maxwidth class. Applied for tablet screen or beyond.

  • Right Button: Hide Sidebar. Applied for tablet screen or beyond.

HTML Content

Our toggler component is simply a HTML document, with a bunch Bulma class, and two feathericons.

  <!-- toggler-->
  <section class="mx-1 my-1">
    <button class="button is-outlined is-pulled-left">
      <span class="icon is-small">
        <i class="fas fa-angle-double-left"></i>
      </span>
    </button>
    <button class="button is-outlined is-pulled-right">
      <span class="icon is-small">
        <i class="fas fa-angle-double-right"></i>
      </span>
    </button>
    <div class="is-clearfix"></div>
  </section>

Bulma MD: Toggler Component

This is just an example. Your are free working with your own design.

Tablet Only

Since toggler is set applied for tablet screen, we can hide the toggler component in mobile screen.

  <section class="mx-1 my-1 is-hidden-mobile">
    ...
  </section>

Button ID

We need to give each button an ID, so that javascript can locate the chosen element. We also need to give a class to detect its state.

Left Button: left_toggler ID with left_toggler_active class.

    <button id="left_toggler"
            class="button is-outlined is-pulled-left
                   left_toggler_active">
      ...
    </button>

Right Button: right_toggler ID with right_toggler_active class.

    <button id="right_toggler"
            class="button is-outlined is-pulled-right
                   right_toggler_active">
      ...
    </button>

Javacript.

Our first javascript example should be simple. This script will handle onclick event on both element, in an unobstrusive fashioned.

document.addEventListener("DOMContentLoaded", function(event) { 
  const leftToggler  = document.getElementById("left_toggler");
  const rightToggler = document.getElementById("right_toggler");

  leftToggler.onclick = function() {
    leftToggler.classList.toggle("left_toggler_active");
    return false;
  }

  rightToggler.onclick = function() {
    rightToggler.classList.toggle("right_toggler_active");
    return false;
  }
});

The problem with custom scripting for specific design is, the script should reflect the html tag from the design. Thus script cannot be portable.

To port for your own design, you need to change with a bit of adjustment.

Inspect Element

You can load the page, click each button, and check whether, the active class is toggled.

Bulma MD: Inspect Toggle Class


3: Toggling Icon

Visual First

Both toggle icon should reflect the state, whether active or inactive. We can utilize different icon, flip the each arrow.

Four State

Now we have four states.

  • Left Right:

Bulma MD:Toggle: Left Right

  • Left Left:

Bulma MD:Toggle: Left Left

  • Right Left:

Bulma MD:Toggle: Right Left

  • Right Right:

Bulma MD:Toggle: Right Right

Main Script

Consider add a few lines to handle onclick event.

  const leftToggler  = document.getElementById("left_toggler");
  const rightToggler = document.getElementById("right_toggler");

  leftToggler.onclick = function() {
    leftToggler.classList.toggle("left_toggler_active"); 

    toggleLeftIcon();
    leftToggler.blur();

    return false;
  }

  rightToggler.onclick = function() {
    rightToggler.classList.toggle("right_toggler_active");

    toggleRightIcon();
    rightToggler.blur();

    return false;
  }

At the end of each script, we are going to blur the focus, so that the clicked element will get clean looks, instead of a bordered icon.

Left Icon

This will flip AwesomeIcon from fa-angle-double-right to fa-angle-double-left, and flip back on the next click.

  function toggleLeftIcon() {
    const isActiveLeft = leftToggler.classList
      .contains("left_toggler_active");

    const leftIcon     = leftToggler
      .getElementsByTagName("i")[0];
    
    if (isActiveLeft) {
      leftIcon.classList.remove("fa-angle-double-right");
      leftIcon.classList.add("fa-angle-double-left");

      console.log("Left toggler class is active");
    } else {
      leftIcon.classList.remove("fa-angle-double-left");
      leftIcon.classList.add("fa-angle-double-right");

      console.log("Left toggler class is inactive");
    }
  }

Right Icon

On the opposite icon, this will flip AwesomeIcon from fa-angle-double-left to fa-angle-double-right, and flip back on the next click.

  function toggleRightIcon() {
    const isActiveRight = rightToggler.classList
      .contains("right_toggler_active");

    const rightIcon     = rightToggler
      .getElementsByTagName("i")[0];
  
    if (isActiveRight) {
      rightIcon.classList.remove("fa-angle-double-left");
      rightIcon.classList.add("fa-angle-double-right");

      console.log("Right toggler class is active");
    } else {
      rightIcon.classList.remove("fa-angle-double-right");
      rightIcon.classList.add("fa-angle-double-left");

      console.log("Right toggler class is inactive");
    }
  }

How does it works?

These functions rely on checking each state, by using isActiveLeft or isActiveRight.

Left Icon:

  function toggleLeftIcon() {
    const isActiveLeft = leftToggler.classList
      .contains("left_toggler_active");
    ...
    
    if (isActiveLeft) {
      ...
    } else {
      ...
    }
  }

Right Icon:

  function toggleRightIcon() {
    const isActiveRight = rightToggler.classList
      .contains("right_toggler_active");
    ...
  
    if (isActiveRight) {
      ...
    } else {
      ...
    }
  }

Inspect Element

You can load the page, click each button, and check the console tab.

Bulma MD: Toggle Console Log


4: Toggling Layout Appearance

This should be functional.

From icon appearance, we go further to explore, what each clicks does. Ecah click would change the layout documents using Bulma class.

HTML Content

Which Layout?

We need to alter a bit our layout, give the respective element a marker class.

Maxwidth: maxwidth_toggler

  <!-- header -->
  <nav role="navigation" aria-label="main navigation"
       class="navbar is-fixed-top is-white maxwidth maxwidth_toggler
              white z-depth-3 hoverable">
    ...
  </nav>

  <!-- main -->
  <div class="columns is-8 layout-base maxwidth maxwidth_toggler">
    ...
  </div>

  <!-- footer -->
  <footer class="site-footer">
    <div class="navbar is-fixed-bottom maxwidth maxwidth_toggler
          is-white has-text-centered is-vcentered
          white z-depth-3 hoverable">
      ...
    </div>
  </footer>

Sidebar: main_toggler, and aside_toggler.

    <main role="main"
          id="main_toggler" 
          class="column is-two-thirds">
      ...
    </main>

    <aside id="aside_toggler"
           class="column is-one-thirds">
      ...
    </aside>

Main Script

Again, add more lines to handle onclick event.

  const leftToggler  = document.getElementById("left_toggler");
  const rightToggler = document.getElementById("right_toggler");

  leftToggler.onclick = function() {
    leftToggler.classList.toggle("left_toggler_active");
 
    toggleLeftIcon();
    toggleLeftLayout(); 

    leftToggler.blur();
    return false;
  }

  rightToggler.onclick = function() {
    rightToggler.classList.toggle("right_toggler_active");

    toggleRightIcon();
    toggleRightLayout();

    rightToggler.blur();
    return false;
  }

At the end of each script, we are going to blur the focus, so that the clicked element will get clean looks, instead of a bordered icon.

Left Layout

Disable maxwidth class.

This function will enable or disable custom made maxwidth class, for use with wide screen

  function toggleLeftLayout() {
    const maxWidthTogglers = document
      .getElementsByClassName("maxwidth_toggler");

    // ECMAScript 2015 
    for (let mwt of maxWidthTogglers) {
      mwt.classList.toggle("maxwidth");
    }
  }

I’m using ECMAScript 2015 for loop example. You can use the old school while loop, for compatibility with old browser.

    const maxWidthTogglers = document
      .getElementsByClassName("maxwidth_toggler);

    var i=0;
    while (i < maxWidthTogglers.length) {
      maxWidthResizers[i].classList.toggle("maxwidth");
      i++;
    }

From the normal view as this below:

Bulma MD: Left Button: Wide Normal

To a wider view as this below, after clicking the left button (disable maxwidth).

Bulma MD: Left Button: Wide Active

And even wider content, after clicking the right button (hiding sidebar), as we wil see in the next on click event handler.

Bulma MD: Both Button: Wide

Right Layout

This is our toggle sidebar wit two task.

  • Hide sidebar with is-hidden-tablet class.

  • Change width of main content from is-two-thirds to is-full class.

  function toggleRightLayout() {
    const isActiveRight = rightToggler.classList
      .contains("right_toggler_active");

    const mainToggler  = document.getElementById("main_toggler");
    const asideToggler = document.getElementById("aside_toggler");
  
    if (isActiveRight) {    
      mainToggler.classList.add("is-two-thirds");
      mainToggler.classList.remove("is-full");

      asideToggler.classList.remove("is-hidden-tablet"); 
    } else {
      mainToggler.classList.add("is-full");
      mainToggler.classList.remove("is-two-thirds");

      asideToggler.classList.add("is-hidden-tablet");
    }
  }

Now from the normal view as below:

Bulma MD: Gap Issue

You can switch to this:

Bulma MD: Right Button: Normal

I usually enjoy wider view while reading on the internet.


5: Fix Gap in Layout

Just in case you didn’t notice, my design has a flaw with code above. The gap between column is bigger than it should be.

It can be fix as below:

Main Script

Again, add more lines to handle onclick event.

  const leftToggler  = document.getElementById("left_toggler");
  const rightToggler = document.getElementById("right_toggler");

  leftToggler.addEventListener("click", () => {
    leftToggler.classList.toggle("left_toggler_active");
 
    toggleLeftIcon();
    toggleLeftLayout(); 
    fixGap();

    leftToggler.blur();
    return false;
  });

  rightToggler.addEventListener("click", () => {
    rightToggler.classList.toggle("right_toggler_active");

    toggleRightIcon();
    toggleRightLayout();
    fixGap();

    rightToggler.blur();
    return false;
  });

Fixing Gap

Problem solved

Fixing gap between two columns, is the harder part of this script, because it require deep knowledge about the design itself.

  function fixGap() {
    const isActiveLeft = leftToggler.classList
      .contains("left_toggler_active");
    const isActiveRight = rightToggler.classList
      .contains("right_toggler_active");

    const mainToggler  = document.getElementById("main_toggler");
    const asideToggler = document.getElementById("aside_toggler");

    mainToggler.classList.remove("pr-0");
    asideToggler.classList.remove("pl-0");

    if (!isActiveLeft && isActiveRight) {
      mainToggler.classList.add("pr-0");
      asideToggler.classList.add("pl-0");
    }
    
    console.log("Fix gap class.");
  }

But don’t worry. It is fixed now.

Bulma MD: Right Button: Active

Official Spacing Helpers

Update

This article series was made in June 2019 with Bulma 0.7.

In June 2020, Bulma 0.9 comes out. And Bulma 0.9 is equipped with spacing helpers.


6: Separation of Concern

Refactoring Consideration

Consider this event handler below:

  leftToggler.addEventListener("click", () => {
    leftToggler.classList.toggle("left_toggler_active");
 
    toggleLeftIcon();
    toggleLeftLayout(); 
    fixGap();

    leftToggler.blur();
    return false;
  });

Why don’t I put the whole thing in just one functions. Of course I can do that, but the script becomes more unreadable as below example.

  function rightResizerToggler() {
    rightResizer.classList
      .toggle("right_resizer_active");

    const isActive   = rightResizer.classList
      .contains("right_resizer_active");

    const rightIcon    = rightResizer.getElementsByTagName("i")[0];
    const mainResizer  = document.getElementById("main_resizer");
    const asideResizer = document.getElementById("aside_resizer");
  
    if (isActive) {
      rightIcon.classList.remove("fa-angle-double-left");
      rightIcon.classList.add("fa-angle-double-right");
      
      mainResizer.classList.add("is-two-thirds");
      mainResizer.classList.remove("is-full");

      asideResizer.classList.remove("is-hidden-tablet"); 

      console.log("right resizer class is active");
    } else {
      rightIcon.classList.remove("fa-angle-double-right");
      rightIcon.classList.add("fa-angle-double-left");

      mainResizer.classList.add("is-full");
      mainResizer.classList.remove("is-two-thirds");

      asideResizer.classList.add("is-hidden-tablet");

      console.log("right resizer class is inactive");
    }
    
    rightResizer.blur();
  }

It is better that we focus each function, to its own task as below example:

  function toggleRightIcon() {
    const isActiveRight = ...
    ...
  }

  function toggleRightLayout() {
    const isActiveRight = ...
    ...
  }

  function fixGap() {
    const isActiveLeft = ...
    const isActiveRight = ...

    ...
  }

Of course there is a trade-off, by repeating those const above. But, it would be easier to understand.


What is Next ?

What do you think?

That is all. Our journey has complete here. Now it is a good time to apply the theme to your favorite framework. But this is a different journey on different article series.

Farewell. We shall meet again.