5

Haizz. Hello guys..

I'm learning Web Dev in: https://www.w3schools.com/.

I did a very simple homework here: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_tabs

```
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
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;
}

/* Style the tab content */
.tabcontent {
  display: none;
  padding: 6px 12px;
  border: 1px solid #ccc;
  border-top: none;
}
</style>
</head>
<body>

<h2>Tabs</h2>
<p>Click on the buttons inside the tabbed menu:</p>

<div 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>

<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";
}
</script>

</body>
</html>
```

But.... When I try to exchange the traditional for-loop to for..in loop. It not work?

Ex. for (i in tabcontent) { tabcontent[i].style.display = "none"; }

After many time I try to make it work. I've found that all statement follow the first for..in loop will be skip !!!???? It mean the Function will auto-break after a for..in loop. The first for..in loop work as normal, but the rest statement after it is simply skip. The Function break at this point?

If anyone knew this problem please help me to understand it. X__X

La Mort
  • 51
  • 3

2 Answers2

5

The "auto-break" you're seeing is an uncaught exception breaking execution – you should have your browser's console open to see any errors that occur and are uncaught.

That change yields an

Uncaught TypeError: Cannot set property 'display' of undefined
    at openCity (<anonymous>:6:33)
    at HTMLButtonElement.onclick (tryit.asp?filename=tryhow_js_tabs:1)

since for..in loops over properties of objects, not array elements as you imagine, and i ends up (as evidenced by console.log(i)) being 0, 1, 2, and then finally length, and tabcontent.length has no style property, so the equivalent of

tabcontent.length.style.display = ...

naturally fails.

AKX
  • 152,115
  • 15
  • 115
  • 172
  • I got it now. Thanks so much. But .. so .. we should not use for..in loop in this case? – La Mort Dec 30 '19 at 16:02
  • 1
    Good explanation, this should be the accepted answer – CoderLee Dec 30 '19 at 16:08
  • 1
    @LaMort Yes, [don't use `for…in` enumerations on array-like structures](https://stackoverflow.com/q/500504/1048572) (for many reasons). You might use `for … of` in modern browsers though. – Bergi Dec 30 '19 at 16:10
  • Yes. I try to `console.log(i)` to see clearly. And it consist of [0, 1, 2, length, Item, nameditem]. :))))) – La Mort Dec 30 '19 at 16:13
  • @Bergi Sorry but i've just test it againt. And for..of bring error too. So.. Just do it with traditional for-loop. ;))) – La Mort Dec 30 '19 at 16:18
1

You can go with the for in loop. The loop will give you indexes but because you have not converted it to an array you get some extra (unwanted properties) hence the error.

The key is to make the conversion, after that you are safe with the for in loop:
let tabContent = Array.from(document.getElementsByClassName("tabcontent"))

Example proof: https://jsfiddle.net/sqfgb0k6/

<!DOCTYPE html>
<html>

  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      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;
      }

      /* Style the tab content */
      .tabcontent {
        display: none;
        padding: 6px 12px;
        border: 1px solid #ccc;
        border-top: none;
      }

    </style>
  </head>

  <body>

    <h2>Tabs</h2>
    <p>Click on the buttons inside the tabbed menu:</p>

    <div 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>

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

    </script>

  </body>

</html>
EugenSunic
  • 13,162
  • 13
  • 64
  • 86