Lesonderdeel 3: Gecombineerde sitenavigatie

Off-Canvas menu

In de vorige twee lesonderdelen hebben we de topnavigatie en het off-canvas menu bekeken. Hierbij werd nog geen rekening gehouden met het combineren van deze menutypes. In dit lesonderdeel gaan we bekijken hoe we deze twee menutypes kunnen combineren zodoende we de gebruikservaring kunnen optimaliseren. In de codesnippet van de html code vinden we alvast de mark up voor de opstelling. In deze worden beide menu's in het bodyelement onder de nav tag geplaatst. Bijkomend voegen we nog een header tag en een main tag eronder.

HTML
Mark up van de topnavigatie en off-canvas menu gecombineerd

<!DOCTYPE html>
<html lang="nl">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Gecombineerde sitenavigatie</title>    
</head>

<body>
    <nav>
        <div class="topnav" id="myTopnav">
            <a href="javascript:void(0);" class="icon" onclick="topnav()">&#9776;</a>
            <a href="index.html" class="active">Start</a>
            <a href="#">HTML</a>
            <a href="#">CSS</a>
            <a href="#">JS</a>
        </div>
        <button class="btn" onclick="sidenav()">&#9776;</button>
        <div id="mySidenav1" class="sidenav">
            <a href="#" class="active">Les 1: Topnavigatie</a>
            <a href="#">Les 2: Off-canvas menu</a>
            <a href="gecombineerde sitenavigatie.html" class="active">Les 3: Gecombineerde sitenavigatie</a>
            <a href="javascript:void(0)" class="closebtn" onclick="sidenav()">&times;</a>
        </div>
    </nav>
    <header>
        <h1>Lesonderdeel 3: Gecombineerde sitenavigatie</h1>
    </header>
    <main>
        <h2>Gecombineerde sitenavigatie</h2>
    </main>
</body>
</html>
Code copied! Html code

Opmaak

In het eerste en het tweede lesonderdeel hadden we de al reeds de opmaak van beide menu's besproken. In dit lesonderdeel voegen nog de opmaak toe aan het body element, het header element en het main element. Indien we de code in deze code in onze editor invoeren, merken we echter dat beide menu's mekaar overlappen bij het bedienen ervan op schermen die kleiner zijn dan 600px . Ook wordt de webcontent bedekt bij het bedienen van het off-canvas menu. Dit zorgt voor verwarring bij de gebruiker. Dit gaan we verder in JS oplossen.

CSS

html {
  font-size: clamp(.8rem, 1.4vw, 2.4rem);
  line-height: 1.6;  
}

body {
  padding: 0;
  margin: 0;
  font-family: "Lato", sans-serif;
}

* {
  box-sizing: border-box;
}

/* topnav */
.topnav {
  overflow: hidden;
  background-color: #333;
}
                              
.topnav a {
  color: white;
  text-decoration: none;
  padding: .6em;
}

.topnav a:hover {
  background-color: #ddd;
  color: #333;
}
                              
.topnav a.active {
  background-color: #ddd;
  color: #333;
}
                              
.topnav a:not(:first-child, :nth-child(2)) {
  display: none;
}
                              
.topnav a:first-child {
  float: right;
  display: block;
}

.topnav a:nth-child(2) {
  display: inline-block; 
} 

.topnavresponsive a:not(:first-child,:nth-child(2)) {
  display: block;  
}
                                                            
@media screen and (min-width: 600px) {
  .topnav a:not(:first-child) {
    display: block;
    float: left;   
  }
                              
  .topnav a:first-child {
    display: none;
  }                              
}

/* Sidenav */
.sidenav a {
  display: block;
  text-decoration: none;
  padding: .2em .5em;
  color: #818181; 
}

.sidenav a:hover {
  background-color: #ddd;
  color: #333; 
}

.sidenav a.active {
  background-color: #ddd;
  color: #333;
}

.sidenav a:first-child {
  margin-top: 3.5em;
}

.sidenav .closebtn {
  position: absolute;
  top: 0;
  right: 0;
  font-size: clamp(2rem, 1vw, 2.4rem);
  border: 1px solid #333;
  padding: 0 .5em;
}

.sidenav {
  height: 100%; 
  background-color: #333;
  position: fixed;
  top: 0;
  left: 0;
  width: 0;
  transition: 0.6s;
  overflow-x: hidden;
  visibility: hidden;  
}

.sidenavOpen {
  width: clamp(200px, 20%, 600px); 
  transition: 0.6s;
  visibility: visible;
}

.btn:focus {
  border: 2px solid #ddd;
}

.btn:hover {
  border: 2px solid #ddd;
}

.btn {
  background-color: white;
  cursor: pointer;
  border-radius: 5px;
  margin: 5px;
  font-size: clamp(2rem, 1vw, 2.4rem);
}

header,main{
  transition: margin-left .6s;  
}
.header,.main{
  margin-left: clamp(200px, 20%, 600px);  
}
h1{
  margin-left: .5em;
  word-break: keep-all;  
}
Code copied! CSS code

Functionaliteit

In de codesnippet is de code die we tijdens de vorige lesonderdelen hebben opgemaakt als commentaar is aangegeven. Dus deze wordt niet meer beschouwd als een coderegel. Daaronder bevindt zich de aangepaste code. Deze code verhinderd dus dat beide menu' mekaar overlappen bij schermen onder de 600px. Om na te gaan hoe deze code werkt gaan we terug naar de console van de developpers tool. We openen de console en zorgen dat het off-canvas menu dichtgeklapt is. We weten uit vorige les dat het off-Canvas menu in open toestand een bijkomende classname draagt nl.(".sidenavOpen").

Om na te gaan op welke eigenschappen van het off-Canvas menu we ons moeten richten controleren we dat het off-canvas menu dichtgeklapt is en voeren document.querySelector(".sidenavOpen") in de console. Merk dat de console de waarde null retourneert. M.a.w. het element is op dit ogenblik onbestaand aangezien we deze selector pas toekennen wanneer we op de bedieningsknop van het menu klikken.

Dus kunnen we stellen dat wanneer we de topnavigatieknop bedienen op schermen die kleiner zijn dan 600px, we eerst dienen na te gaan of de selector (".sidenavOpen") van het off-Canvas menu al dan niet de waarde Null draagt en indien dit niet het geval is het off-Canvas eerst dient te sluiten. Om deze toestand vast te stellen maken we gebruik van een if-else statement. We zouden dan kunnen stellen dat volgende code zou werken.

if (document.querySelector(".sidenavOpen")===null){
console.log ('of-canvas is closed')
};

We zorgen dat het off-canvas menu nu opengeklapt is en andersom maken we gebruik van de not operator !. De code zou er dan als volgt uitzien:

if (document.querySelector(".sidenavOpen")!==null){
console.log ('of-canvas is open')
};

In Javascript staan de drie === gelijkheidstekens voor de gelijkheidheid van de waarde en het type van de vergeleken elementen. Voor demonstratieredenen heb ik in dit voorbeeld de primitieve null waarde gebruikt maar deze kan echter weggelaten worden. Zie codesnippet.

We hebben ondertussen kunnen achterhalen hoe we de toestand van het off-Canvas menu via JS kunnen vaststellen maar onze code zal pas functioneren wanneer we een instrustie toevoegen en een else statement toevoegen. De instructie nemen we over uit de code die we tijdens het eerste lesonderdeel gezien hebben en voor de else statement doen we hetzelfde met het verschil dat we de functie sidenav() van het tweede lesonderdeel hergebruiken door deze bovenaan de instructie toe te voegen onder het else statement. Dit noemen we een functie oproepen. We voeren dit principe nu ook uit om de toestand van de topnavigatie vast te stellen.

JavaScript
null waarde in console null waarde met if-statement achterhalen in console
                                  

// function topnav() {
  //   document.querySelector(".topnav").classList.toggle("topnavresponsive");
  //  }
  
  function topnav() {

    if (!document.querySelector(".sidenavOpen")) {

      document.querySelector(".topnav").classList.toggle("topnavresponsive");

    } else {

      sidenav()
      document.querySelector(".topnav").classList.toggle("topnavresponsive");
    }
  }

//function sidenav() {
  //  document.querySelector(".sidenav").classList.toggle("sidenavOpen");
  //}

  function sidenav() {

    if (!document.querySelector(".topnavresponsive")) {
      document.querySelector(".sidenav").classList.toggle("sidenavOpen");

    } else {

      topnav()
      document.querySelector(".sidenav").classList.toggle("sidenavOpen");
    }
  }
Code copied! JS code

Functionaliteit optimaliseren

Tot nu hebben we voor het oproepen van de functies beroep gedaan op het onclick attribuut. Dit heeft echter enkele beperkingen. Met de addEventListener functie kunnen we namelijk meerdere events aan één selector toevoegen. Later komt dit van pas bij het inlogformulier. Merk dat het onclick attribuut in de html markup van beide menu's is verwijderd.

HTML

<!DOCTYPE html>
<html lang="nl">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Gecombineerde sitenavigatie</title>    
</head>

<body>
    <nav>
        <div class="topnav" id="myTopnav">
            <a href="javascript:void(0);" class="icon">&#9776;</a>
            <a href="index.html" class="active">Start</a>
            <a href="#">HTML</a>
            <a href="#">CSS</a>
            <a href="#">JS</a>
        </div>

        <button class="btn">&#9776;</button>

        <div id="mySidenav1" class="sidenav">
            <a href="#" class="active">Les 1: Topnavigatie</a>
            <a href="#">Les 2: Off-canvas menu</a>
            <a href="gecombineerde sitenavigatie.html" class="active">Les 3: Gecombineerde sitenavigatie</a>
            <a href="javascript:void(0)" class="closebtn">&times;</a>
        </div>
    </nav>
    <header>
        <h1>Lesonderdeel 3: Gecombineerde sitenavigatie</h1>
    </header>
    <main>
        <h2>Gecombineerde sitenavigatie</h2>
    </main>
</body>
</html>
Code copied! Html code

Er zijn namelijk een tal van eventlisteners. In dit onderdeel gebruiken we de versie van het onclick event die we tijdens de vorige lesonderdelen al gezien hebben. Het verschil is dat wanneer we de functie addEventListener gebruiken we in de functie aangeven dat het om een 'click' event gaat i.p.v. een 'onclick' event zoals bij de eerdere opstelling in de html markup. Beide verwijzen dus naar het zelfde event. De functie addEventListener draagt dus 2 parameters. Het type event en een bijkomende functie dat uitgevoerd moet worden wanneer het type event plaatsneemt.

Alvorens we de codesnippet in onze editor kopieriëren voeren we de volgende code in de console van de developpers tools en klikken op het icoon van de topnavigatie dat verschijnt wanneer de schermgrootte >= 600px.

const topnavElement=document.querySelector(".icon")
topnavElement.addEventListener('click',function(){
console.log ('Dit is een eventlistener')
});

In de codesnippet kan je merken dat de tweede parameter “function()” vervangen is door “()=>”. Beide verwijzen naar een functie maar met het verschil dat de eerste notatie ietwat verouderd is maar niettegenstaand toch nog in sommige gevallen erg nuttig kan zijn. Meer hierover bij het volgend lesonderdeel.

We roepen binnen de eventlistener nu de eerder gemaakte functies op door deze toe te voegen. We herhalen dit voor alle bedieningsknoppen.

                                  

const topnavElement=document.querySelector(".icon")
// topnavElement.addEventListener('click',function(){ 
// }); 
  topnavElement.addEventListener('click',()=>{
    topnav();  
  });
  
function topnav() {
  if (!document.querySelector(".sidenavOpen")) {
    document.querySelector(".topnav").classList.toggle("topnavresponsive");
  } else {
    sidenav()
    document.querySelector(".topnav").classList.toggle("topnavresponsive");
  }
}

const sidenavElementOpen=document.querySelector(".btn")
  sidenavElementOpen.addEventListener('click',()=>{
    console.log(sidenavElementOpen)
    sidenav();  
  });

const sidenavElementClose=document.querySelector(".closebtn")
  sidenavElementClose.addEventListener('click',()=>{
    sidenav();  
  });

function sidenav() {
  if (!document.querySelector(".topnavresponsive")) {

    document.querySelector(".sidenav").classList.toggle("sidenavOpen");
    
  } else {

    topnav()
    document.querySelector(".sidenav").classList.toggle("sidenavOpen")
  }
}
Code copied! JS code