How can I make it so only the tab area loads instead of the page jumping to the top after clicking on tab?

87 views Asked by At

As the title states, I'm making a tabbed section to switch content upon click which works fine, how can I make it so upon clicking a new tab it has a smooth transition to the content as well as prevent jumping to the top of the page every time I click a tab?

I've tried adding the function which prevents it for links but this isn't a link so that doesn't seem to be working.

HTML

 <section class="featured-books">
        <div class="featured-books-title"><h2>Featured Books</h2></div>
        <ul class="tabs">
            <li data-tab-target="#featured" class="active tab">Featured</li>
            <li data-tab-target="#on-sale" class="tab">On Sale</li>
            <li data-tab-target="#most-viewed" class="tab">Most Viewed</li>
        </ul>
        <div id="featured" data-tab-content class="active">
            <div class="featured-tab">
                <img src="./images/12-rules.jpg">
                <img src="./images/7-habits.jpg">
                <img src="./images/art-of-war.jpg">
                <img src="./images/boundaries.jpg">
                <img src="./images/unlimited-memory.jpg">
                <img src="./images/meaning-of-marriage.jpg">
                <img src="./images/meditations.jpg">
                <img src="./images/peaceful-parents.jpg">
                <img src="./images/plant-paradox.jpg">
                <img src="./images/spirit-filled-life.jpg">
                <img src="./images/javascript-definitive-guide.jpg">
                <img src="./images/atomic-habits.jpg">
            </div>
        </div>
        <div id="on-sale" data-tab-content>
        </div>
        <div id="most-viewed" data-tab-content>
        </div>
    </section>

CSS

.featured-books h1 {
    display: flex;
    justify-content: center;
}

[data-tab-content] {
    display: none;
}
    
.active[data-tab-content] {
    display: block;
}
    
.tabs {
    display: flex;
    justify-content: center;
    list-style-type: none;
    margin: 0;
    padding-bottom: 60px;
    padding-top: 16px;
}


.tab {
    border-radius: 20px;
    cursor: pointer;
    padding: 10px;
}

.tab.active {
    background-color: #CCC;
}

.tab:hover {
    background-color: #aaa;
}



/**------FEATURED TAB CONTENT------*/

.featured-tab {
    position: absolute;
    justify-content: center;
    align-items: center;
    margin-top: 10px;
    width: 100vw;
    display: grid;
    grid-template-columns: repeat(auto-fill,minmax(300px,300px));
    column-gap: 3px;
    row-gap: 40px;
  }
  
  .featured-tab img {
    width: 180px;
    height: auto;
    object-fit: cover;
    object-position: center;
  } 

JavaScript

const tabContents = document.querySelectorAll('[data-tab-content]')
  
  tabs.forEach(tab => {
    tab.addEventListener('click', () => {
      const target = document.querySelector(tab.dataset.tabTarget)
      tabContents.forEach(tabContent => {
      tabContent.classList.remove('active')
    })
    tabs.forEach(tab => {
      tab.classList.remove('active')
    })
    tab.classList.add('active')
    target.classList.add('active')
  })
}) 
1

There are 1 answers

4
KJEK-Code On

Here is a simple example using a opacity transition but you can use height, width or transform if you would like. I use aria-attributes to keep track of things like which article is open and if the information in the article should be picked up by screen readers. The two most important CSS classes are show and hide. These control the opacity and when the transition takes place. Show has a slight delay so it waits for the one being hidden to get out of the way. As far as the JavaScript.

  1. Select all the buttons that have popups.
  2. Create a event listener to handle the click.
  3. Select the controlled article and all the articles.
  4. Check if the controlled article is currently hidden.
  5. If it is hide all the artiles.
  6. Change all the buttons aria-expanded attributes to false.
  7. Set the aria-expanded attribute on the clicked button to true.
  8. Set aria-hidden class on the controlled article to false.
  9. Remove the hide class and add the show class to the controlled article.

const buttons = document.querySelectorAll("[aria-haspopup=true]")

const handleClick = (event) => {
  const controls = event.target.getAttribute("aria-controls"),
  controlled = document.getElementById(controls),
  articles = document.querySelectorAll("article");
  if (controlled.getAttribute("aria-hidden") === "true") {
    articles.forEach(article => {
      article.setAttribute("aria-hidden", "true");
      article.classList.add("hide");
      article.classList.remove("show");
    })
    buttons.forEach(button => button.setAttribute("aria-expanded", "false"))
    event.target.setAttribute("aria-expanded", "true");
    controlled.setAttribute("aria-hidden", "false");
    controlled.classList.remove("hide");
    controlled.classList.add("show");
  }
}

buttons.forEach(button => {
  button.addEventListener("click", handleClick);
})
ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
}

li {
  margin-right: 10px;
}

article {
  height: calc(100vh - 50px);
  width: 100vw;
  position: absolute;
  top: 50px;
  left: 0;
}

#feature {
  background-color: red;
}

#sale {
  background-color: green;
}

#view {
  background-color: blue;
}

.show {
  opacity: 1;
  transition: opacity .2s ease-in-out .2s;
}

.hide {
  opacity: 0;
  transition: opacity .2s ease-in-out;
}

button[aria-expanded=true] {
  background-color:  #cceeff;
}
<ul>
  <li>
    <button aria-haspopup="true" aria-expanded="true" aria-controls="feature">Featured</button>
  </li>
  <li>
    <button aria-haspopup="true" aria-expanded="false" aria-controls="sale">On Sale</button>
  </li>
  <li>
    <button aria-haspopup="true" aria-expanded="false" aria-controls="view">Most Viewed</button>
  </li>
</ul>
<article class="show" id="feature" aria-hidden="false">
  <h1>Featured</h1>
</article>
<article class="hide" id="sale" aria-hidden="true">
  <h1>On Sale</h1>
</article>
<article class="hide" id="view" aria-hidden="true">
  <h1>Most Viewed</h1>
</article>