Creating a custom function to manipulate the style.display property of a div element

I'm attempting to write a function that can achieve the functionality shown in this example without repeating code. I want to create tabs that display content based on which tab is clicked, similar to this: https://www.w3schools.com/howto/howto_js_tabs.asp, but without using onClick directly in the HTML. While I am able to toggle the active class for buttons, I'm struggling with changing the style.display property of the tab content. The current code works as intended, but I believe there is a more elegant solution.

What adjustments should I make to my openTab function to achieve the desired outcome?
Is there a better approach for adding event listeners to the buttons?

document.getElementById("btn1").addEventListener("click", function() {
  document.getElementById("content1").className ="show";
  document.getElementById("content2").className ="tabcontent";
  document.getElementById("content3").className ="tabcontent";
});
document.getElementById("btn2").addEventListener("click", function() {
  document.getElementById("content1").className ="tabcontent";
  document.getElementById("content2").className ="show";
  document.getElementById("content3").className ="tabcontent";
});
document.getElementById("btn3").addEventListener("click", function() {
  document.getElementById("content1").className ="tabcontent";
  document.getElementById("content2").className ="tabcontent";
  document.getElementById("content3").className ="show";
});
function openTab(evt) {
    // Declare all variables
  var i, tabcontent, tablinks;

  // Get all elements with class="tabcontent" and hide them
  tabcontent = document.getElementsByClassName("tabcontent");
  for (i = 0; i < tabcontent.length; i++) {
    tabcontent[i].style.display = "none";
    console.log('first')
  }
      
  // Get all elements with class="tablinks" and remove the class "active"
  tablinks = document.getElementsByClassName("tablinks");
  for (i = 0; i < tablinks.length; i++) {
    tablinks[i].className = tablinks[i].className.replace(" active", "");
  }
  // Show the current tab, and add an "active" class to the button that opened the tab
 

  evt.currentTarget.className += " active";

 //this loop adds a style.display = "block" to every tabcontent class.
  for (i=0; tabcontent.length; i++) {
    tabcontent[i].style.display = "block";
  }
  //if I make the id's for all the divs "content" this line adds the style.display = "block" to 
    only the first tabcontent class

  //document.getElementById("content").style.display = "block";
  
  
}
document.getElementById("btn1").addEventListener("click", openTab);
document.getElementById("btn2").addEventListener("click", openTab);
document.getElementById("btn3").addEventListener("click", openTab);


<div class="tab-container">
  <button class="tablinks" id="btn1">Fit Guide</button>
  <button class="tablinks" id="btn2">Care</button>
  <button class="tablinks" id="btn3">Material</button>
</div>
<div id="content1" class="tabcontent">
  <p>Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>
<div id="content2" class="tabcontent">
  <p>Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>
<div id="content3" class="tabcontent">
  <p>Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>

Answer №1

When it comes to adding event listeners to a group of similar elements, there is a more organized approach that can be taken. One way to do this is by selecting all the elements by their class name and then iterating over each element to add an event listener. Here's an example:

const buttons = [...document.getElementsByClassName("tablinks")];

buttons.forEach(button => {
  button.addEventListener("click", () => {
      // Your code here
  });
});

A key point to remember is that getElementsByClassName returns an HTML Collection, which needs to be converted into an iterable using the spread operator.

In regards to ensuring the tabs display the correct content, your approach is generally on track. Using classList instead of other methods might provide cleaner code, and you may not always need to iterate through all the elements.

I've provided an additional example for clarification. Feel free to reach out if you have any questions!

const toggleActive = (buttonId) => {
  const buttonClicked = document.getElementById(buttonId);
  const currentActiveBtn = document.getElementsByClassName("active")[0];

  currentActiveBtn.classList.remove("active");
  buttonClicked.classList.add("active")

}

const toggleShow = (contentId) => {
  const contentToShow = document.getElementById(contentId);
    const currentShownContent = document.getElementsByClassName("show")[0];
  
  currentShownContent.classList.remove("show");
  contentToShow.classList.add("show")
}

const buttons = [...document.getElementsByClassName("tablinks")];

buttons.forEach(button => {
  button.addEventListener("click", () => {
    toggleActive(button.id)
    
    const contentId = "content" + button.id.charAt(button.id.length - 1);
    
    toggleShow(contentId);
  });

})
.tabcontent {
  display: none;
}

.active {
  color: red;
}

.show {
  display: block
}
<div class="tab-container">
  <button class="tablinks active" id="btn1">Fit Guide</button>
  <button class="tablinks" id="btn2">Care</button>
  <button class="tablinks" id="btn3">Material</button>
</div>
<div id="content1" class="tabcontent show">
  <p>Content for Tab 1</p>
  <p>Hello World from Tab 1!</p>
</div>
<div id="content2" class="tabcontent">
  <p>Content for Tab 2</p>
  <p>Hello Again from Tab 2!!</p>
</div>
<div id="content3" class="tabcontent">
  <p>Content for Tab 3</p>
  <p>Okay Bye now!</p>
</div>

Answer №2

A more organized approach involves setting a single event handler on the container element that holds all the buttons and using event delegation to detect the click event (during the bubbling phase)

let tc = document.querySelector('.tab-container');

tc.addEventListener('click', function(ev) {
  let target = ev.target;
  if (target.matches('button[data-idcontent]')) {
    let idContent = target.dataset.idcontent;
    
    /* hide previous visible content (if any) */
    let prevVisible = document.querySelector('.tabcontent.visible');
    if (!!prevVisible && prevVisible.id !== idContent) {
      prevVisible.classList.remove('visible');
    }
    
    document.getElementById(idContent).classList.add('visible');
   
  }
});
.tabcontent:not(.visible) {
  display: none;
}
<div class="tab-container">
  <button class="tablinks" data-idcontent="content1">Fit Guide</button>
  <button class="tablinks" data-idcontent="content2">Care</button>
  <button class="tablinks" data-idcontent="content3">Material</button>
</div>

<div id="content1" class="tabcontent">
  <p>1 Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>
<div id="content2" class="tabcontent">
  <p>2 Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>
<div id="content3" class="tabcontent">
  <p>3 Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>


CSS-only alternative

Another option would be to achieve the same functionality using only CSS, by utilizing the :target pseudo-class along with links instead of buttons (though you can always style a link to look like a button)

.tabcontent:not(:target) {
  display: none;
}

.tab-container a {
  display: inline-block;
  border: 1px currentColor solid;
  border-radius: 3px;
  background: #e8e8e8;
  padding: .15em .5em;
  text-decoration: none;
  color: #000;
  font: .8em Arial;
}
<div class="tab-container">
  <a href="#content1">Fit Guide</a>
  <a href="#content2">Care</a>
  <a href="#content3">Material</a>
</div>

<div id="content1" class="tabcontent">
  <p>1 Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>
<div id="content2" class="tabcontent">
  <p>2 Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>
<div id="content3" class="tabcontent">
  <p>3 Integer vel arcu ac dolor tincidunt dapibus..</p>
</div>

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

Dynamic creation of HTML/Ionic checkbox leads to ng-change not binding properly

Recently, my team and I have been actively engaged in the process of handling an XML file and dynamically constructing a settings page based on the information extracted from it. Allow me to present an illustration of how these elements are dynamically cre ...

How can the outer function be connected to the resolve method of $routeProvider?

Here is a functional code snippet: $routeProvider.when('/clients', { templateUrl:'/views/clients.html', controller:'clientsController', resolve: { rights: function ( ...

Maximize Rotation - JavaScript Rotation

Currently tackling a Codewars challenge. The task involves finding the maximum possible result after performing a rotation on a given number. This rotation is unique in that 'n' number of digits will remain fixed after each rotation, with &apos ...

Issue with zeroLineColor and zeroLineWidth not functioning properly for the x-axis within Chartjs

Looking to customize the width and color of the x-axis in Chartjs? While it's straightforward for the y-axis using zeroLineColor and zeroLineWidth, achieving the same effect for the x-axis seems trickier. If you try adding: ticks: { beginAtZero: tr ...

Validate form for radio group

Hello Experts, I am currently working on developing a form that includes a JavaScript validation function for a radio group. The main objective is to have a division pop up when either "Monday" or "Tuesday" is checked, and no popup when "None" is selected ...

The input 'Query' cannot be matched with the type '[(options?: QueryLazyOptions<Exact<{ where?:"

Using codegen, I've created custom GraphQL hooks and types. query loadUsers($where: UserFilter) { users(where: $where) { nodes { id email firstName lastName phoneNumber } totalCount } } export functio ...

Tips for creating a Vue component that triggers the select dropdown to open when the entire div or wrapper is clicked

I have a custom-designed select dropdown with unique symbols for the select arrow. To achieve this look, I've hidden the default select arrow/triangle and placed our symbol on top as an image file. <div class="select-wrapper"> < ...

Utilizing dual functions within the onChange event handler in React

I have a situation where I need to pass a function from a parent component to a child component through the onChange event, as well as another function in the child component to update its own state. How can I achieve this? Parent export function Fruits() ...

The CSS property for setting the background color on the body element is not functioning properly

I am attempting to make changes to my app.scss file in my Symfony 4 project. Unfortunately, the background-color property is not being applied as expected. body { background-color: #ccccff; } In my base.html.twig file, I have the following structure: ...

Updating the rotational pivot of an object following a positional shift

After moving my object with the "w" key and then rotating it with the "x" key, I noticed that my object rotates around the world origin instead of its own center. How can I update the object's pivot after moving it? I've come across suggestions t ...

Is it possible to swap out the content within a <div> element with an external piece of HTML code using JQuery or JavaScript whenever the viewport dimensions are adjusted?

<html> <head> </head> function () { var viewportWidth = $(window).width(); if (viewportWidth < 700) { $('#wrapper').load('A.html'); }; <body> &l ...

What steps are involved in incorporating dynamic pagination in PHP using this particular snippet of code?

I am looking for a way to display a list of numbers from 1 to 1000 in a single line with the ability to scroll. However, I need to exclude both "Prev" and "1" from this list. How can I achieve this? Below is the code snippet that I currently have: echo ...

The counter unexpectedly increments by 2 instead of 1

I'm attempting to create a counter that increases by one each time according to the code snippet below: var imnum = 0; (function changeImage() { ++imnum; $( ".slider" ).fadeOut(5000, function() { $('#home-slider-im').attr("s ...

In-depth preg_match_all analysis

I'm encountering an issue with my detailed preg_match_all not working as expected. Instead of retrieving the desired data, I am getting a blank Array. Below is the code snippet I'm struggling with: <?php $remote_search = file_get_content ...

I'm experiencing an issue where only the lower half of my website is visible on three out of five browsers. Why is this happening?

Working on my portfolio website at www.magalidb.0fees.net, I am facing challenges with the display in different browsers. The issue arises when only the bottom half of the content within a container is visible, while the top half remains out of view or mis ...

Error: React Js Module Not Found

Encountered a Compilation Failure. The module was not found: Error: An attempt to import ../components/App which exists outside the src/ directory of the project has been made. Relative imports from outside src/ are not permitted. To resolve this issue, c ...

Ajax is capable of sending images as part of the data payload

I am attempting to send data via Ajax, and this data may include an image. Unfortunately, I do not have any forms, so I am unable to submit in the traditional way. Here is my HTML code: <input type="file" accept="image/png,image/jpg,image/jpeg,image/ ...

JavaScript library unsuccessful in transferring data to PHP script

I am facing an issue while trying to transfer variables from javascript to a php file for execution. The problem is that the php file is not being called or executed, even though I have confirmed that it works properly when tested individually. $(function ...

Discovering a sophisticated way to locate a span element using the not(p) selector in Selenium

Within a span element with an ID, there is a mixture of text and a paragraph tag. The goal is to use selenium webdriver to pinpoint the street address in the mix below: <span id="myspan"> <p>fullname</p> street address - has no ...

What is the method for showing the arrays following Json.stringify and Json.parse?

I've been facing challenges trying to redirect notifications from my mobile application to another page and store them as historical records. Despite my efforts in researching and experimenting with various methods, I have yet to achieve the desired o ...