0

Starting from a basic example from W3Schools, I would like to automatically add a new tab in my horizontal menu. It has three tabs: “London”, “Paris”, and “Tokyo”. I want to programmatically add a new one: “Rome”.

I have created a button that calls a piece of JavaScript code. Here’s the whole code:

body {
  font-family: Arial;
}


/* Style the tab */

.tab {
  overflow: hidden;
  border: 1px solid #ccc;
  background-color: #f1f1f1;
}


/* Style the buttons inside the tab */

.tab button {
  background-color: inherit;
  float: left;
  border: none;
  outline: none;
  cursor: pointer;
  padding: 14px 16px;
  transition: 0.3s;
  font-size: 17px;
}


/* Change background color of buttons on hover */

.tab button:hover {
  background-color: #ddd;
}


/* Create an active/current tablink class */

.tab button.active {
  background-color: #ccc;
}


/* Create an active new tablink class */

.tab button.new {
  background-color: #eaaa00;
  font-weight: bold;
}


/* Style the tab content */

.tabcontent {
  display: none;
  padding: 6px 12px;
  border: 1px solid #ccc;
  border-top: none;
}
<h2>Tabs</h2>
<p>Click on the buttons inside the tabbed menu:</p>

<div id="HorizonTab" class="tab">
  <button class="tablinks" onclick="openCity(event, 'London')">London</button>
  <button class="tablinks" onclick="openCity(event, 'Paris')">Paris</button>
  <button class="tablinks" onclick="openCity(event, 'Tokyo')">Tokyo</button>
</div>

<div id="London" class="tabcontent">
  <h3>London</h3>
  <p>London is the capital city of England.</p>
</div>

<div id="Paris" class="tabcontent">
  <h3>Paris</h3>
  <p>Paris is the capital of France.</p>
</div>

<div id="Tokyo" class="tabcontent">
  <h3>Tokyo</h3>
  <p>Tokyo is the capital of Japan.</p>
</div>
<br>
<input type="button" value="Add new city" onclick="newCityTab(event, 'Rome')">

<script>
  function openCity(evt, cityName) {
    var i, tabcontent, tablinks;
    
    tabcontent = document.getElementsByClassName("tabcontent");
    
    for (i = 0; i < tabcontent.length; i++) {
      tabcontent[i].style.display = "none";
    }
    
    tablinks = document.getElementsByClassName("tablinks");
    
    for (i = 0; i < tablinks.length; i++) {
      tablinks[i].className = tablinks[i].className.replace(" active", "");
    }
    
    document.getElementById(cityName).style.display = "block";
    evt.currentTarget.className += " active";
  }

  function newCityTab(evt, cityName) {
    var tab = document.getElementById("HorizonTab");
    var newTab = document.createElement("button");
    
    newTab.setAttribute("class", "tablinks");
    newTab.setAttribute("onclick", "openCity(event, " + cityName + ")");
    newTab.setAttribute("style", "display: block;");
    newTab.textContent = cityName;
    tab.appendChild(newTab);

    // Add new div to the body, content for new cityName
    var newDivCity = document.createElement("div");
    
    newDivCity.setAttribute("id", cityName);
    newDivCity.setAttribute("class", "tabcontent");
    
    var h3 = document.createElement("h3");
    
    h3.textContent = cityName;
    newDivCity.appendChild(h3);
    
    var p = document.createElement("p");
    
    p.textContent = cityName + " is another capital";
    newDivCity.appendChild(p);

    var lastCityDiv = document.getElementById('Tokyo');
    
    lastCityDiv.parentNode.insertBefore(newDivCity, lastCityDiv.nextSibling)
  }
</script>

It works pretty much, except that my newly created tab “Rome” does not implement exactly the same behavior as the 3 others (“London”, “Paris”, “Tokyo”, created statically in the initial HTML code).

When I inspect the code in the developer tools, I notice that the newly created tab doesn’t behave correctly from the point of view of CSS events, (display: block or display: none). My new tab seems to be blocked at display: none.

What is missing or wrong in my code to make it work better?

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
  • 1
    Your code should be posted **here**, not on an external site. – Pointy Oct 30 '22 at 13:27
  • Aside from the above, look at the HTML generated for your new button and compare it to the originals. Hint: quotes. – Rory McCrossan Oct 30 '22 at 13:28
  • 2
    That being said, you should *really* be using a delegated unobtrusive event handler for this, not an outdated `onclick` attribute: https://stackoverflow.com/a/27373951/519413 – Rory McCrossan Oct 30 '22 at 13:29
  • @Rory McCrossan. Thanks for you answer. I see the problem, but How could I write the event handler in my code ? – user2160279 Oct 30 '22 at 13:44
  • And why the question seems to be closed? ??? – user2160279 Oct 30 '22 at 13:48
  • Use [event delegation](//developer.mozilla.org/en/docs/Learn/JavaScript/Building_blocks/Events#Event_delegation) instead of adding several event listeners — it’s more maintainable and applies to dynamically added elements. See [the tag info](/tags/event-delegation/info) and [this Q&A](/q/1687296/4642212). Use the [event argument](//developer.mozilla.org/en/docs/Web/API/EventTarget/addEventListener#The_event_listener_callback). – Sebastian Simon Oct 30 '22 at 14:41
  • `document.getElementById("HorizonTab").addEventListener("click", ({`[`target`](//developer.mozilla.org/en/docs/Web/API/Event/target)`}) => { const tab = target.closest(".tablinks"); if(tab){ console.log("You clicked", tab, "and its text content is", tab.textContent); } });`. Then do whatever you need to do with `tab` and `tab.textContent` inside the `if` statement; I’m sure you can adapt this code. Don’t forget to remove the obsolete `onclick` attributes (which were also using the deprecated global [`event`](//developer.mozilla.org/en/docs/Web/API/Window/event)). – Sebastian Simon Oct 30 '22 at 14:41

0 Answers0