Skip to content

Why do I have to click my event handler target twice before the DOM is updated?

I am making a time tracking dashboard site, it has 3 links (Daily, Weekly, and Monthly) and I want the content to change based on which link is clicked. I am setting hash values for all 3 links as #daily, #weekly, and #monthly and in my jQuery script I am hiding and showing divs by adding if conditions that uses location.hash property to hide and show specific. The script is working somewhat but the content changes only when I double click on a link, I don’t know what is wrong with my script. I have made a repository and also enabled GitHub pages for it, GitHub Repository and GitHub Pages Link.

let daily = document.getElementsByClassName('daily');
let weekly = document.getElementsByClassName('weekly');
let monthly = document.getElementsByClassName('monthly');

window.onload = function() {
  $(weekly).hide();
  $(monthly).hide();
};

function hideAndShow() {
  if (location.hash === '#daily') {
    $(daily).show();
    $(weekly).hide();
    $(monthly).hide();
  } else if (location.hash === '#weekly') {
    $(daily).hide();
    $(weekly).show();;
    $(monthly).hide();
  } else if (location.hash === '#monthly') {
    $(daily).hide();
    $(weekly).hide();
    $(monthly).show();
  }
};

let frequency = document.querySelectorAll('.user__frequency__link');
for (let i = 0; i < frequency.length; i++) {
  frequency[i].addEventListener('click', function(_event) {
    hideAndShow(this);
  });
}
@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500&display=swap');
*,
*::after,
*::before {
  box-sizing: border-box;
}

:root {
  --clr-neutral: hsl(0, 100%, 100%);
  --clr-primary-100: hsl(236, 100%, 87%);
  --clr-primary-200: hsl(235, 45%, 61%);
  --clr-primary-300: hsl(246, 80%, 60%);
  --clr-primary-400: hsl(235, 46%, 20%);
  --clr-primary-500: hsl(226, 43%, 10%);
  --clr-work: hsl(15, 100%, 70%);
  --clr-play: hsl(195, 74%, 62%);
  --clr-study: hsl(348, 100%, 68%);
  --clr-exercise: hsl(145, 58%, 55%);
  --clr-social: hsl(264, 64%, 52%);
  --clr-self-care: hsl(43, 84%, 65%);
}

body {
  font-family: 'Rubik', sans-serif;
  background-color: var(--clr-primary-500);
  color: var(--clr-neutral);
}

h1 {
  margin: 0;
}

a {
  text-decoration: none;
  color: var(--clr-neutral);
}

.bg-primary-400 {
  background-color: var(--clr-primary-400);
  margin-top: 2.8rem;
  border-radius: 1.25rem 1.25rem 0 0;
}

.user__frequency__link {
  font-size: 22px;
  font-weight: 400;
  opacity: .5;
}

.user__frequency__link:visited,
.user__frequency__link:focus,
.user__frequency__link:hover,
.user__frequency__link:active {
  opacity: 1;
}

.container {
  margin: 5rem 1rem;
}

.rounded-box {
  border-radius: 1.5rem;
  overflow: hidden;
}

.rounded-box+.rounded-box {
  margin-top: 1.6rem;
}

.box-padding {
  padding: 1rem 2rem;
}

.box-padding1 {
  padding: 3.4rem 2rem;
}

.box-padding2 {
  padding: 1rem 2rem 2rem 2rem;
}

.user__name {
  background-color: var(--clr-primary-300);
  border-radius: 0 0 1.25rem 1.25rem;
  display: flex;
  gap: 2rem;
  align-items: center;
  justify-content: center;
}

.user__img {
  border: 2px solid var(--clr-neutral);
  border-radius: 50%;
  width: 125px;
  height: 125px;
}

.user__frequency {
  display: flex;
  justify-content: space-around;
}

.user {
  background-color: var(--clr-primary-400);
}

.row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.heading-neutral {
  font-size: 1.125rem;
  font-weight: 500;
  margin-bottom: 0.846rem;
}

.ellipsis {
  color: var(--clr-neutral);
}

.work {
  background-color: var(--clr-work);
  background-image: url(../images/icon-work.svg);
  background-repeat: no-repeat;
  background-position: 95% -7%;
}

.play {
  background-color: var(--clr-play);
  background-image: url(../images/icon-play.svg);
  background-repeat: no-repeat;
  background-position: 95% -2%;
}

.study {
  background-color: var(--clr-study);
  background-image: url(../images/icon-study.svg);
  background-repeat: no-repeat;
  background-position: 95% -7%;
}

.exercise {
  background-color: var(--clr-exercise);
  background-image: url(../images/icon-exercise.svg);
  background-repeat: no-repeat;
  background-position: 95% 0%;
}

.social {
  background-color: var(--clr-social);
  background-image: url(../images/icon-social.svg);
  background-repeat: no-repeat;
  background-position: 95% -7%;
}

.self-care {
  background-color: var(--clr-self-care);
  background-image: url(../images/icon-self-care.svg);
  background-repeat: no-repeat;
  background-position: 95% -7%;
}

.daily,
.weekly,
.monthly {
  display: flex;
  flex-grow: 1;
  align-items: center;
}

.current {
  font-size: 2rem;
  font-weight: 300;
}

.previous {
  margin-left: auto;
  opacity: 0.6;
}


/* .weekly,
.monthly {
   display: none;
} */

.attribution {
  font-size: 11px;
  text-align: center;
}

.attribution a {
  color: hsl(228, 45%, 44%);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
  <div class="user rounded-box">
    <div class="user__frequency box-padding">
      <h3><a href="#daily" class="user__frequency__link" id="user__frequency__daily" class="box-padding">Daily</a></h3>
      <h3><a href="#weekly" class="user__frequency__link" id="user__frequency__weekly" class="box-padding">Weekly</a></h3>
      <h3><a href="#monthly" class="user__frequency__link" id="user__frequency__monthly" class="box-padding">Monthly</a></h3>
    </div>
  </div>

  <div class="work rounded-box">
    <div class="bg-primary-400 box-padding2">
      <div class="row">
        <h2 class="heading-neutral">Work</h2>
        <a href=""><img src="./images/icon-ellipsis.svg" alt=""></a>
      </div>
      <div class="row">
        <div id="work__daily" class="daily">
          <div class="current">5hrs</div>
          <!-- daily -->
          <div class="previous">Yesterday - 7hrs</div>
          <!-- daily -->
        </div>
        <div id="work__weekly" class="weekly">
          <div class="current">32hrs</div>
          <!-- weekly -->
          <div class="previous">Last Week - 36hrs</div>
          <!-- weekly -->
        </div>
        <div id="work__monthly" class="monthly">
          <div class="current">103hrs</div>
          <!-- monthly -->
          <div class="previous">Last Month - 128hrs</div>
          <!-- monthly -->
        </div>
      </div>
    </div>
  </div>

  <div class="play rounded-box">
    <div class="bg-primary-400 box-padding2">
      <div class="row">
        <h2 class="heading-neutral">Play</h2>
        <a href=""><img src="./images/icon-ellipsis.svg" alt=""></a>
      </div>
      <div class="row">
        <div id="play__daily" class="daily">
          <div class="current">1hr</div>
          <!-- daily -->
          <div class="previous">Yesterday - 2hrs</div>
          <!-- daily -->
        </div>
        <div id="play__weekly" class="weekly">
          <div class="current">10hrs</div>
          <!-- weekly -->
          <div class="previous">Last Week - 8hrs</div>
          <!-- weekly -->
        </div>
        <div id="play__monthly" class="monthly">
          <div class="current">23hrs</div>
          <!-- monthly -->
          <div class="previous">Last Month - 29hrs</div>
          <!-- monthly -->
        </div>
      </div>
    </div>
  </div>

  <div class="study rounded-box">
    <div class="bg-primary-400 box-padding2">
      <div class="row">
        <h2 class="heading-neutral">Study</h2>
        <a href=""><img src="./images/icon-ellipsis.svg" alt=""></a>
      </div>
      <div class="row">
        <div id="study__daily" class="daily">
          <div class="current">0hrs</div>
          <!-- daily -->
          <div class="previous">Yesterday - 1hr</div>
          <!-- daily -->
        </div>
        <div id="study__weekly" class="weekly">
          <div class="current">4hrs</div>
          <!-- weekly -->
          <div class="previous">Last Week - 7hrs</div>
          <!-- weekly -->
        </div>
        <div id="study__monthly" class="monthly">
          <div class="current">13hrs</div>
          <!-- monthly -->
          <div class="previous">Last Month - 19hrs</div>
          <!-- monthly -->
        </div>
      </div>
    </div>
  </div>

  <div class="exercise rounded-box">
    <div class="bg-primary-400 box-padding2">
      <div class="row">
        <h2 class="heading-neutral">Exercise</h2>
        <a href=""><img src="./images/icon-ellipsis.svg" alt=""></a>
      </div>
      <div class="row">
        <div id="exercise__daily" class="daily">
          <div class="current">1hr</div>
          <!-- daily -->
          <div class="previous">Yesterday - 1hr</div>
          <!-- daily -->
        </div>
        <div id="exercise__weekly" class="weekly">
          <div class="current">4hrs</div>
          <!-- weekly -->
          <div class="previous">Last Week - 5hrs</div>
          <!-- weekly -->
        </div>
        <div id="exercise__monthly" class="monthly">
          <div class="current">11hrs</div>
          <!-- monthly -->
          <div class="previous">Last Month - 18hrs</div>
          <!-- monthly -->
        </div>
      </div>
    </div>
  </div>

  <div class="social rounded-box">
    <div class="bg-primary-400 box-padding2">
      <div class="row">
        <h2 class="heading-neutral">Social</h2>
        <a href=""><img src="./images/icon-ellipsis.svg" alt=""></a>
      </div>
      <div class="row">
        <div id="social__daily" class="daily">
          <div class="current">1hr</div>
          <!-- daily -->
          <div class="previous">Yesterday - 3hrs</div>
          <!-- daily -->
        </div>
        <div id="social__weekly" class="weekly">
          <div class="current">5hrs</div>
          <!-- weekly -->
          <div class="previous">Last Week - 10hrs</div>
          <!-- weekly -->
        </div>
        <div id="social__monthly" class="monthly">
          <div class="current">21hrs</div>
          <!-- monthly -->
          <div class="previous">Last Month - 23hrs</div>
          <!-- monthly -->
        </div>
      </div>
    </div>
  </div>

  <div class="self-care rounded-box">
    <div class="bg-primary-400 box-padding2">
      <div class="row">
        <h2 class="heading-neutral">Self Care</h2>
        <a href=""><img class="" src="./images/icon-ellipsis.svg" alt=""></a>
      </div>
      <div class="row">
        <div id="self-care__daily" class="daily">
          <div class="current">0hrs</div>
          <!-- daily -->
          <div class="previous">Yesterday - 1hr</div>
          <!-- daily -->
        </div>
        <div id="self-care__weekly" class="weekly">
          <div class="current">2hrs</div>
          <!-- weekly -->
          <div class="previous">Last Week - 2hrs</div>
          <!-- weekly -->
        </div>
        <div id="self-care__monthly" class="monthly">
          <div class="current">7hrs</div>
          <!-- monthly -->
          <div class="previous">Last Month - 11hrs</div>
          <!-- monthly -->
        </div>
      </div>
    </div>
  </div>
</div>

<div class="attribution">
  Challenge by <a href="https://www.frontendmentor.io?ref=challenge" target="_blank">Frontend Mentor</a>. Coded by <a href="#">Your Name Here</a>.
</div>

Answer

The problem really is just a race condition basically… You check the location hash before it gets updated by the click on the a. But, is that really what you’d like to do? Change the display according to the hash value, on a user click? Wouldn’t you be better to just listen to hash changes, and act accordingly?

window.addEventListener("hashchange", hideAndShow);

Every time the hash changes, hideAndShow will trigger and do the changes needed… That way you do not have to remember to add X or Y events on every click that changes the hash value.