简体   繁体   中英

Writing a function to change style.display property of a div?

I am trying to write a function that performs the below code without repeating itself. I want to make tabs that display content depending on what tab you click on like this: https://www.w3schools.com/howto/howto_js_tabs.asp but without the onClick in the HTML. I am able to toggle the class of the buttons to add the active class to them, but I can't figure out how to change the style.display property for the tabcontent. The first bit of code performs what I want, but I know there is a cleaner solution.

How do I get my openTab function to do what I want?
Is there a better way to add the 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>

As to the question of adding event listeners to a collection of similar elements, there is indeed a cleaner way. You can get all the elements by classname and iterate over each element while adding an event listener to it. That will look something as follows:

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

buttons.forEach(button => {
  button.addEventListener("click", () => {
      // Some Code Here
  });
});

Important to note is that getElementsByClassName returns anHTML Collection and so it is important to convert it to an iterable, in our case using the spread operator.

As to the other question of getting the tabs to show the correct content, you are more or less on the right path, I would just use classList instead, and there is no real need to iterate over all the elements.

I've included an example as well, let me know 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>

A cleaner approach is to set just one event handler on the element wrapping all the buttons and to detect the click event by event delegation (in 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 approach

As a side note you could obtain the same behaviour in CSS only (which can save you from using JS at all) with the :target pseudoclass, if you used links instead of buttons (but you can always style a link as 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM