Css using display style with fade-in animation togather

I developed an HTML code and I intend to display the text related to each link with a fade-in animation when the link is clicked.

In this code, I have a card with two columns. The first column contains 5 links, and the second column contains 5 text elements wrapped in

tags. In the second column, only one

tag should be visible at a time.

When I don’t use the display style, all the

tags remain in the DOM, and the fade-in animation works correctly. However, all the text and

tags that are not related to the active link should not be displayed. For this purpose, when I use the display style, the fade-in animation doesn’t work properly.

const links = document.querySelectorAll('.links a');
const content = document.querySelectorAll('.content p');

function showContent(index) {
  links.forEach((link, i) => {
    if (i === index - 1) {
      link.classList.add('active');
    } else {
      link.classList.remove('active');
    }
  });

  content.forEach((c, i) => {
    if (i === index - 1) {
      c.classList.add('active');
    } else {
      c.classList.remove('active');
    }
  });
}
.container {
  display: flex;
  width: 80%;
  margin: 40px auto;
}

.links {
  width: 20%;
  background-color: #f0f0f0;
  padding: 20px;
}

.links a {
  text-decoration: none;
  color: #000;
  padding: 10px;
  display: block;
  border-bottom: 1px solid #ccc;
}

.links a.active {
  background-color: #ff4444;
  color: #fff;
}

.content {
  width: 80%;
  padding: 20px;
}

.content p {
  font-size: 18px;
  color: #333;
  opacity: 0;
  transition: opacity 0.5s ease-in-out;
  display: none;
}

.content p.active {
  display: block;
  opacity: 1;
}
<div class="container">
  <div class="links">
    <a href="#" class="active" onclick="showContent(1)">Link (1)</a>
    <a href="#" onclick="showContent(2)">Link (2)</a>
    <a href="#" onclick="showContent(3)">Link (3)</a>
    <a href="#" onclick="showContent(4)">Link (4)</a>
    <a href="#" onclick="showContent(5)">Link (5)</a>
  </div>
  <div class="content">
    <p id="content-1" class="active">Text related with Link (1)</p>
    <p id="content-2">Text related with Link (2)</p>
    <p id="content-3">Text related with Link (3)</p>
    <p id="content-4">Text related with Link (4)</p>
    <p id="content-5">Text related with Link (5)</p>
  </div>
</div>

2

Firefox cannot animate display

display can be animated with transition-behavior: allow-discrete; and @starting-style for all the major browsers sans Firefox.

There are three examples:

  1. JavaScript with <button>s
  2. CSS with <a>s
  3. CSS with <input type="radio">s & <label>s

Example 1 uses Event Delegation — the event handler listens for “click” events but only actually reacts whenever a <button> is clicked. All examples use the following CSS:

/**
 * Default state.
 */
position: absolute;
visibility: hidden;
opacity: 0;

/**
 * Active state.
 */
position: static;
visibility: visible;
opacity: 0;
transition: 
  0.2s position,
  0.7s visibility ease-in,
  0.7s opacity ease-in

Instead of using display: block/none which ignores animation, use:

  • position: absolute/static to bring the elements in and out of the document flow
  • visibility: hidden/visible to mask the flashing of content

Details are commented in the examples.

JavaScript
<button></button>

// Reference section.box
const box = document.querySelector(".box");
/**
 * This "click" event handler toggles the .active class on
 * the clicked <button> and the corresponding <article>
 * @param {object} event - Event object
 */
const fadeInItem = (event) => {
  // event.target determines what the user actually clicked
  const clk = event.target;
  /**
   * If the user clicked a <button>...
   */
  if (clk.matches("button")) {
    // make an array of all <button>s...
    const btns = [...document.querySelectorAll("button")];
    // make an array of all <article>s...
    const arts = [...document.querySelectorAll("article")];
    // remove .active from all <button>s and <article>s...
    btns.forEach((btn, i) => {
      btn.classList.remove("active");
      arts[i].classList.remove("active");
    });
    // find the index number of the <button> the user clicked...
    const idx = btns.indexOf(clk);
    // add .active to the <button> the user clicked and...
    btns[idx].classList.add("active");
    // to the <article> at the same index number.
    arts[idx].classList.add("active");
  }
};

/**
 * Register .box to listen for the "click" event if fired on
 * itself and any of it's children. When triggered, it will
 * will call function fadeInItem().
 */
box.addEventListener("click", fadeInItem);
:root {
  font: 2.5vmax/1.2 "Segoe UI"
}

.box {
  display: flex;
  margin: 5rem auto;
}

nav {
  background-color: #f0f0f0;
}

menu {
  list-style: none;
  padding: 0
}

li {
  border-bottom: 1px solid #ccc;
}

button {
  display: block;
  padding: 0.75rem;
  border: 0;
}

.content {
  padding: 0.75rem;
}

/**
 * PROPERTIES/VALUES
 * position: absolute is the default state of <article> 
 * which takes it out of the document flow -- it doesn't 
 * take up space in the DOM, hence shifting is minimal.
 */
article {
  position: absolute;
  font-size: 1.25rem;
  visibility: hidden;
  opacity: 0;
}

button.active {
  background-color: #ff4444;
  color: #fff;
}

/**
 * PROPERTIES/VALUES
 * The transition of position to/from static/absolute 
 * should be quick (ex. 0.2ms) so that it's sudden 
 * insertion/removal to/from the DOM will not linger and 
 * give off a flash of the content that's leaving. The 
 * visibility to/from visible/hidden is there to mask any
 * shifting of content. 
 */
article.active {
  position: static;
  visibility: visible;
  opacity: 1;
  transition: 
    position 0.2s, 
    visibility 0.7s ease-in, 
    opacity 0.7s ease-in;
}
<section class="box">
  <nav>
    <menu>
      <li>
        <button class="active">Link (1)</button>
      </li>
      <li>
        <button>Link (2)</button>
      </li>
      <li>
        <button>Link (3)</button>
      </li>
      <li>
        <button>Link (4)</button>
      </li>
      <li>
        <button>Link (5)</button>
      </li>
    </menu>
  </nav>
  <section class="content">
    <article class="active">
      <p>Text related with Link (1)</p>
    </article>
    <article>
      <p>Text related with Link (2)</p>
    </article>
    <article>
      <p>Text related with Link (3)</p>
    </article>
    <article>
      <p>Text related with Link (4)</p>
    </article>
    <article>
      <p>Text related with Link (5)</p>
    </article>
  </section>
</section>

CSS
<a href="#ID" id="ID"></a>

:root {
  font: 2.5vmax/1.2 "Segoe UI"
}

/**
 * PROPERTIES/VALUES
 * When an <a> is clicked everything jumps to the top. 
 * position: relative/top: 5rem allows everything to shift 
 * to the top but everything will stop shifting after that. 
 * The flex-items (ex. <a>) will flow vertically due to
 * flex-flow: column... (aka flex-direction).
 */
.box {
  position: relative;
  top: 5rem;
  display: flex;
  flex-flow: column nowrap;
  width: 80%;
  margin: auto;
}

nav {
  background-color: #f0f0f0;
}

menu {
  list-style: none;
  padding: 0;
}

li {
  border-bottom: 1px solid #ccc;
}

a {
  display: block;
  width: 20%;
  padding: 0.75rem;
  border-bottom: 2px solid #ccc;
  text-decoration: none;
}

/**
 * PROPERTIES/VALUES
 * position: absolute/left: 30% takes it out of document 
 * flow so it doesn't sit under the <a>s (because of flex 
 * column direction of .box) and instead sits to the right
 * of the <a>s
 */
.content {
  position: absolute;
  left: 30%;
  margin: -0.75rem 0 0;
}

a:link,
a:visited {
  color: #000;
}

a:target {
  background-color: #ff4444;
  color: #fff;
}

/**
 * PROPERTIES/VALUES
 * position: absolute is the default state of <article> 
 * which takes it out of the document flow -- it doesn't 
 * take up space in the DOM, hence shifting is minimal.
 */
article {
  position: absolute;
  font-size: 1.125rem;
  visibility: hidden;
  opacity: 0;
}

/**
 * SELECTORS
 * If #item{N} <a> is clicked, find the next .content
 * then find the <article> that's the :nth-child({N}).
 */
/**
 * PROPERTIES/VALUES
 * The transition of position to/from static/absolute 
 * should be quick (ex. 0.2ms) so that it's sudden 
 * insertion/removal to/from the DOM will not linger and 
 * give off a flash of the content that's leaving. The 
 * visibility to/from visible/hidden is there to mask any
 * shifting of content. 
 */
a#item1:target~.content article:nth-child(1),
a#item2:target~.content article:nth-child(2),
a#item3:target~.content article:nth-child(3),
a#item4:target~.content article:nth-child(4),
a#item5:target~.content article:nth-child(5) {
  position: static;
  visibility: visible;
  opacity: 1;
  transition: 
    position 0.2s, 
    visibility 0.7s ease-in, 
    opacity 0.7s ease-in;
}
<section class="box">
  <!--
    <a href="#ID" id="ID"></a>
    Due to the cascading nature of CSS, the <a> must be 
    placed before and adjacent to either the target 
    (ex. <article>) or ancestors of said target 
    (ex. .content). Each <a>'s [href] is targeting it's own
    [id] so that when clicked it's :target state will stay
    until another `<a>`'s :target state is activated. 
    An <a>'s :target state will change the <article> that 
    is in the same position as the <a> that has the current
    :target state.
  -->
  <a href="#item1" id="item1">Link (1)</a>
  <a href="#item2" id="item2">Link (2)</a>
  <a href="#item3" id="item3">Link (3)</a>
  <a href="#item4" id="item4">Link (4)</a>
  <a href="#item5" id="item5">Link (5)</a>
  <section class="content">
    <article>
      <p>Text related with Link (1)</p>
    </article>
    <article>
      <p>Text related with Link (2)</p>
    </article>
    <article>
      <p>Text related with Link (3)</p>
    </article>
    <article>
      <p>Text related with Link (4)</p>
    </article>
    <article>
      <p>Text related with Link (5)</p>
    </article>
  </section>
</section>

CSS
<input id="ID" type="radio">
&
<label for="ID"></label>

:root {
  font: 2.5vmax/1.2 "Segoe UI"
}

.box {
  display: flex;
  width: 80vw;
  margin: 5rem auto;
}

[name="items"] {
  display: none
}

nav {
  background-color: #f0f0f0;
}

menu {
  list-style: none;
  padding: 0;
}

li {
  border-bottom: 1px solid #ccc;
}

label {
  display: block;
  padding: 0.75rem;
}

.content {
  padding: 0.75rem;
}

/**
 * PROPERTIES/VALUES
 * position: absolute is the default state of <article> 
 * which takes it out of the document flow -- it doesn't 
 * take up space in the DOM, hence shifting is minimal.
 */
article {
  position: absolute;
  font-size: 1.125rem;
  visibility: hidden;
  opacity: 0;
}

/**
 * SELECTORS
 * If #item{N} radio button is :checked, find the first 
 * ~ <nav> after it, then find the <menu> inside of the 
 * <nav>, then find the <li> inside of the <menu> that is 
 * the :nth-child({N}).
 */
#item1:checked~nav menu li:nth-child(1),
#item2:checked~nav menu li:nth-child(2),
#item3:checked~nav menu li:nth-child(3),
#item4:checked~nav menu li:nth-child(4),
#item5:checked~nav menu li:nth-child(5) {
  background-color: #ff4444;
  color: #fff;
}

/**
 * SELECTORS
 * If #item{N} is radio button is :checked, find the
 * first ~ .content after it then find the <article>
 * that's the :nth-child({N}).
 */
/**
 * The transition of position to/from static/absolute 
 * should be quick (ex. 0.2ms) so that it's sudden 
 * insertion/removal to/from the DOM will not linger and 
 * give off a flash of the content that's leaving. The 
 * visibility to/from visible/hidden is there to mask any
 * shifting of content. 
 */
#item1:checked~.content article:nth-child(1),
#item2:checked~.content article:nth-child(2),
#item3:checked~.content article:nth-child(3),
#item4:checked~.content article:nth-child(4),
#item5:checked~.content article:nth-child(5) {
  position: static;
  visibility: visible;
  opacity: 1;
  transition: 
    position 0.2s, 
    visibility 0.7s ease-in, 
    opacity 0.7s ease-in;
}
<section class="box">
  <!-- 
    <input id="ID" type="radio">
    All radio buttons are invisible and not within
    the DOM. Although technically they're not there,
    they still influence whatever is after them in the 
    DOM by whether they are :checked or not.
  -->
  <input id="item1" name="items" type="radio" checked>
  <input id="item2" name="items" type="radio">
  <input id="item3" name="items" type="radio">
  <input id="item4" name="items" type="radio">
  <input id="item5" name="items" type="radio">
  <nav>
    <menu>
      <!--
        <label for="ID"></label>
        Each <label> is associated to the radio button 
        by the [for] attribute's value which is the #id 
        of said radio button. This association allows the 
        radio button to be un/checked by the user clicking
        the associated <label>.
      -->
      <li>
        <label for="item1">Link (1)</label>
      </li>
      <li>
        <label for="item2">Link (2)</label>
      </li>
      <li>
        <label for="item3">Link (3)</label>
      </li>
      <li>
        <label for="item4">Link (4)</label>
      </li>
      <li>
        <label for="item5">Link (5)</label>
      </li>
    </menu>
  </nav>
  <section class="content">
    <article>
      <p>Text related with Link (1)</p>
    </article>
    <article>
      <p>Text related with Link (2)</p>
    </article>
    <article>
      <p>Text related with Link (3)</p>
    </article>
    <article>
      <p>Text related with Link (4)</p>
    </article>
    <article>
      <p>Text related with Link (5)</p>
    </article>
  </section>
</section>

CSS has not supported animation of the display property until quite recently. Most major browsers have added support for transition-behavior: allow-discrete and @starting-style, which are the tools you need to solve this problem. (Firefox is the exception here, but I would proceed on the basis that it will get there soonish.)

document.querySelector('button').addEventListener('click', evt => {
  evt.target.closest('section').classList.toggle('active')
})
:root {
  --box-width: 60px;
}

body, button {
  font-family: sans-serif;
  font-size: 16px;
}

button {
  color: white;
  background: blue;
  border: 0;
  border-radius: 0.5em;
  padding: 0.5em 1em;
  cursor: pointer;
}

div {
  display: flex;
  gap: 0.5em;
}

span {
  color: white;
  background: red;
  width: var(--box-width);
  aspect-ratio: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: 0.7s;
}

.x {
  display: none;
  width: 0;
  opacity: 0;
  transition-behavior: allow-discrete;  
}

.active .x {
  display: flex;
  opacity: 1;
  width: var(--box-width);
  @starting-style {
    opacity: 0;
    width: 0;
  }
}
<section>
  <p><button>Toggle</button></p>
  <div>
    <span>1</span>
    <span class="x">2</span>
    <span>3</span>
  </div>
</section>

5

Using opacity should be fine in this case. I changed the function a bit, so that it is only the anchor that gets a new class name. The content is shown using a CSS selector where I combine :has() with the next-sibling combinator.

const links = document.querySelector('.links');

links.addEventListener('click', e => {
  e.preventDefault();
  if (e.target.nodeName == 'A') {
    let id = e.target.getAttribute('href');
    showContent(id);
  }
});

function showContent(id) {
  links.querySelectorAll('a').forEach(link => link.classList.remove('active'));
  links.querySelector(`a[href="${id}"]`).classList.add('active');
}
.container {
  display: flex;
  width: 80%;
  margin: 40px auto;
}

.links {
  width: 20%;
  background-color: #f0f0f0;
  padding: 20px;
}

.links a {
  text-decoration: none;
  color: #000;
  padding: 10px;
  display: block;
  border-bottom: 1px solid #ccc;
}

.links a.active {
  background-color: #ff4444;
  color: #fff;
}

.content {
  width: 80%;
  padding: 20px;
  position: relative;
}

.content p {
  font-size: 18px;
  color: #333;
  transition: opacity 0.5s ease-in-out;
  position: absolute;
  top: 0;
}

div.links:has(a)+div.content>p {
  opacity: 0;
}

div.links:has(a.active[href="1"])+div.content>#content-1,
div.links:has(a.active[href="2"])+div.content>#content-2,
div.links:has(a.active[href="3"])+div.content>#content-3,
div.links:has(a.active[href="4"])+div.content>#content-4,
div.links:has(a.active[href="5"])+div.content>#content-5 {
  opacity: 1;
}
<div class="container">
  <div class="links">
    <a href="1" class="active">Link (1)</a>
    <a href="2">Link (2)</a>
    <a href="3">Link (3)</a>
    <a href="4">Link (4)</a>
    <a href="5">Link (5)</a>
  </div>
  <div class="content">
    <p id="content-1" class="active">Text related with Link (1)</p>
    <p id="content-2">Text related with Link (2)</p>
    <p id="content-3">Text related with Link (3)</p>
    <p id="content-4">Text related with Link (4)</p>
    <p id="content-5">Text related with Link (5)</p>
  </div>
</div>

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật