JavaScript and CSS tabs with smooth transition effect

I want to create tabs that smoothly fade between each other when switching. The code I have works, but there's a slight issue. When transitioning from one tab to the previous one, there is a momentary glitch where the last tab briefly changes state before fading.

The HTML:

function openSvc(evt, svc) {
  var i, x, tablinks, opacitys;
  x = document.getElementsByClassName("svc");
  for (i = 0; i < x.length; i++) {
    x[i].classList.remove('display-yes');
  }
  tablinks = document.getElementsByClassName("tablink");
  for (i = 0; i < x.length; i++) {
    tablinks[i].className = tablinks[i].className.replace(" active", "");
  }
  document.getElementById(svc).classList.add('display-yes');
  evt.currentTarget.className += " active";
}
.svc {
  opacity: 0;
  position: absolute;
  transition: .3s;
}

.display-yes {
  opacity: 1;
  position: relative;
}
<div class="tab">
  <button class="tablink" onclick="openSvc(event,'dev')"> Software Development </button>
  <button class="tablink active" onclick="openSvc(event,'it')"> IT Infrastructures </button>
  <button class="tablink" onclick="openSvc(event,'design')"> UX / UI Design </button>
  <button class="tablink" onclick="openSvc(event,'consult')"> Consultancy </button>
</div>

<div id="dev" class="svc display-yes">
  <p>lorem ipsum1

</div>

<div id="it" class="svc">
  <p>lorem ipsum2
</div>
<div id="design" class="svc">
  <p>lorem ipsum3

</div>

<div id="consult" class="svc">
  <p>lorem ipsum4

</div>

View the example here: https://jsfiddle.net/4fz01c2o/

Your assistance is greatly appreciated!

Answer №1

It's not entirely clear what type of effect you're aiming for in this scenario. While G-Cyrillus' solution does work, it appears that some of the fade effect is lost.

To maintain that effect, you should eliminate the position: relative rule that was assigned to .display-yes.

This rule was causing issues with the div's positioning. Therefore, you'll need to introduce a container with position: relative; to ensure the .svc divs stay in their designated places.

function openSvc(evt, svc) {
  var i, x, tablinks, opacitys;
  x = document.getElementsByClassName("svc");
  for (i = 0; i < x.length; i++) {
    x[i].classList.remove('display-yes');
  }
  tablinks = document.getElementsByClassName("tablink");
  for (i = 0; i < x.length; i++) {
    tablinks[i].className = tablinks[i].className.replace(" active", "");
  }
  document.getElementById(svc).classList.add('display-yes');
  evt.currentTarget.className += " active";
}
.svc-container {
  position: relative;
  width: 500px;
  height: 200px;
  margin-top: 1rem;
  background-color: lightblue;
}

.svc {
  opacity: 0;
  position: absolute;
  top: 0;
  left: 0;
  transition: .3s;
}

.svc p {
  margin: 0; /* To be consistent with the next block*/
}

.display-yes {
  opacity: 1;
}

.tablink {
  background-color: blue;
}

.active {
  background-color: red;
}
<div class="tab">
  <button class="tablink active" onclick="openSvc(event,'dev')"> Desenvolvimento de software </button>
  <button class="tablink" onclick="openSvc(event,'it')"> Infraestruturas IT </button>
  <button class="tablink" onclick="openSvc(event,'design')"> UX / UI Design </button>
  <button class="tablink" onclick="openSvc(event,'consult')"> Consultoria </button>
</div>
<div class="svc-container">
  <div id="dev" class="svc display-yes">
  <p>lorem ipsum1</p>
  </div>
  <div id="it" class="svc">
    <p>lorem ipsum2</p>
  </div>
  <div id="design" class="svc">
    <p>lorem ipsum3</p>
  </div>
  <div id="consult" class="svc">
    <p>lorem ipsum4</p>
  </div>
</div>

<div style="width:500px; height:200px; background-color:#c2c2c2;">
  <p>
    lorem ipsum lorem ipsum
  </p>

</div>

Answer №2

There are various issues with your current approach: using position absolute for hidden elements can lead to display problems. I recommend creating a parent div with consistent size and color, where each tab is positioned absolutely inside.

Your question is somewhat unclear, but it seems like you want the next tab to fade in only after the previous one has finished fading out. To achieve this, you can use a transitionend event to detect when the fade-out animation completes.

I also suggest implementing event delegation for all tab buttons using data attributes in HTML, which can make your code more robust and reduce the amount of JavaScript required.

To test this code (with a one-second transition), consider:

const tabButtons = document.querySelector('div.tab')
  ,   buttonTabs = document.querySelectorAll('div.tab > button.tablink')
  ,   tabData    = [...document.querySelectorAll('div.svc')].reduce((res,eTab)=>
        {
        res.push( { id:eTab.id, disp:eTab.classList.contains('display-yes'), elm: eTab} )
        return res
        },[])

tabButtons.onclick =e=>
  {
  if (!e.target.matches('button.tablink'))   return // ignore other clicks outside 

  let currentTab = tabData.find(tb=>tb.disp)
    , newTab     = tabData.find(tb=>tb.id===e.target.dataset.tabId)   

  if (newTab.id != currentTab.id )
    {
    currentTab.elm.addEventListener('transitionend',setNewTab)
    currentTab.elm.classList.remove('display-yes')
    currentTab.disp = false

    buttonTabs.forEach(bt=>bt.classList.remove('active'))
    e.target.classList.add('active')
    }
  function setNewTab()
    {
    currentTab.elm.removeEventListener('transitionend',setNewTab)
    newTab.elm.classList.add('display-yes')
    newTab.disp = true
    }
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Learn the process of incorporating animations into text with Material UI

I'm looking to incorporate a 3D animation effect with a 360-degree rotation on some text using Material UI. <Typography variant="overline" color="secondary" style={{fontFamily:'Roboto'}}> All about your needs </Typography> Ho ...

Instructions for selecting an <option> within a <select> element using Python

I am trying to select a specific option by id, but I am encountering an issue when dealing with the elements above the select dropdown. <ul style="display: block; visibility: visible;"><li><a href="#" index="0" class="" onclick="s_objectID= ...

Effectively handling server downtime with AngularJS $resource

I've implemented this code in my services.js file: angular.module('appServices', ['ngResource']). factory('User',function ($resource) { return $resource('http://localhost\\:3001/api/user/:id', { ...

Leveraging Font Awesome and Material Icons within ngFor in Angular 2

I have a unique situation where I need to display a dynamic list of icons in a right side bar. These icons are added to the sidebar based on user actions and are displayed using the ngFor directive. The challenge here is that some icons come from Font Awe ...

Submit a HTML form to a Telegram recipient

I am looking to send HTML form data, which includes input values and select options, to a telegram user. After some research, I discovered that I need to create a Telegram bot. I successfully created one using @botFather by following these steps: /newbot ...

EJS.JS Error: Unable to find the title

I'm facing an issue with a script in express. I have a function that renders a view upon the success of another function. This project involves angular, node, express, and ejs as the view engine. However, when I try to render the view, I encounter an ...

What is the best way to add hidden columns in Telerik Grid MVC3?

I'm currently working with a grid where I need to hide certain columns using the following code: foreach (var attr in grid.Attr) .Columns(columns => { columns.Bound(attr.key) .Width(attr.width) .Visible(attr.isVisi ...

How the logo's placement shifts while zooming out (using CSS and Angular 4+)

I am facing an issue with my navbar that includes a logo (MostafaOmar) which shifts position when zoomed out. If you try zooming to 70%, you will notice the logo's position changes as well. Is there a way to make it stay in place at 100% zoom? Here ...

Refreshing the page causes JavaScript to fail loading

Recently, I encountered a puzzling error. Upon visiting this link The carousel fails to load properly near the bottom of the page. However, if you click on the logo or navigate back to the home page, it works fine. Additionally, performing a command + r ...

Unable to get the Gtranslate function to function properly within the drop-down menu

Currently, I am using Gtranslate.io languages on my website with flags displayed without a drop-down menu. Everything is running smoothly but now I am looking to enhance the user experience by placing the flags inside a drop-down list. I want English to ...

Extracting data from a dropdown menu and displaying it

I've created a form with a select tag that pulls options from a .csv file. However, I'm struggling to retrieve the selected option and display it later in the form. The variable to hold the selected currency is named $selectedCurrency. UPDATE: ...

What is the procedure for transferring the inputted data from an HTML file to its corresponding TS file and subsequently to a different component file?

I have created two components, a login and a home-page. I am attempting to capture user input from the login template, pass it to the login component, and then display it on the home-page template using the home-page component. What is the best approach to ...

Experiencing difficulties with capturing the focus event of the <select> tag

I'm completely stumped at the moment... I just can't seem to get my head around how to make a jQuery $("thing").is(":focus") selector work with a <select> tag in my project. Here's what I've got so far: <input id="uText" name ...

List the acceptable favicon formats for Internet Explorer version 10 and above

When I say type, I'm referring to the file extension. My favicon displays correctly in Firefox, Chrome, and Safari, but someone suggested that the issue may be related to the favicon type. Is there a reliable online resource where I can find informa ...

AngularJS Gallery Showcase

Trying to showcase a collection of videos in a modal using angular.js, I decided to implement the solution from . However, I encountered difficulty in integrating my scope variables into the "html5gallery" class. <div class="html5gallery" data-skin="ho ...

Get a URL from the JSON data returned by the Wikipedia API

How can I retrieve the image URL from a JSON response and store it in a variable? I found helpful information on the MediaWiki API help page Following this example to extract image information from a page: https://commons.wikimedia.org/w/api.php?action= ...

Creating a JavaScript array filled with database data using PHP

Below is a snippet of PHP code used to establish a connection to a database: <?php $host = "localhost"; $user = "admin"; $password = ""; $dbname = "test"; $con = new mysqli($host, $user, $password, $dbname) or die ('Could not connect to the d ...

Is it possible to populate the blank cells in the weekday columns for previous and following months in a mat-datepicker or mat-calendar's display?

In order to enhance user experience, I am designing a calendar that allows users to select dates. My goal is to populate the empty cells at the beginning of the first week with dates from the previous and next months. For this project, I am utilizing the ...

Can one access non-static methods within React Navigation's navigationOptions?

Within my form, I am trying to position a submit button to the right of the header that triggers the same method as the submit button located at the bottom of the form. React Navigation necessitates the declaration of a static method named navigationOptio ...

Unable to locate an element on the webpage due to a JavaScript-based error, which then becomes hidden after a few seconds. (Registration form)

While completing a registration form, I encounter a hidden message after clicking on the register button. Struggling to locate this elusive element has been an ongoing challenge for me. Unfortunately, my attempts to find the element have been unsuccessful ...