Unable to call function declared in the same parent function (DOM)

Viewed 55

The problem is solved. It has nothing to do with the function, but a CSS precedence issue.

I declared a function called alternateTableColor() inside the anonymous function assigned to window.onload. This function alternates the color of a table on the webpage. When it's called without being nested to any other functions, it works fine. But when it's nested in another function declared in the same environment, it does not work. Can you help?

function my$(id) {
  return document.getElementById(id);
}

function deleteTr(oTr) {
  oTr.parentNode.removeChild(oTr);
}
  let currentId = 2;
  let oTable = document.getElementsByTagName('table')[0];
  let aAs = oTable.tBodies[0].getElementsByTagName('a');

  function alternateTableColor() {
    for (let i = 0; i < oTable.tBodies[0].rows.length; i++) {
      if (i % 2) {
        oTable.tBodies[0].rows[i].style.backgroundColor = 'lightgrey';
      } else {
        oTable.tBodies[0].rows[i].style.backgroundColor = '';
      }
    }
  }
  alternateTableColor();
  // add event to existing delete button
  for (let i = 0; i < aAs.length; i++) {
    aAs[i].onclick = function() {
      deleteTr(this.parentNode.parentNode);
    }
  }
  // add event to create button
  my$('create-entry').onclick = function() {
    // add all html data to a data array
    let data = [];
    data.push(++currentId);
    data.push(my$('add-name').value);
    data.push(my$('add-age').value);
    data.push('<a href="javascript:void(0)">Delete</a>');

    // create a <tr> element
    let oTr = document.createElement('tr');
    for (let i = 0; i < data.length; i++) {
      let oTd = document.createElement('td');
      oTd.innerHTML = data[i];
      oTr.appendChild(oTd);
    }
    oTable.tBodies[0].appendChild(oTr);

    // add click event to <a>Delete</a>
    let oA = oTr.getElementsByTagName('a')[0];
    oA.onclick = function() {
      deleteTr(this.parentNode.parentNode);
    }
  };
  // add event to search button
  my$('search-query').onclick = function() {
    alternateTableColor();
    let aQueries = my$('query').value.split(' ');
    console.log(aQueries);
    for (let i = 0; i < oTable.tBodies[0].rows.length; i++) {
      let source = (oTable.tBodies[0].rows[i].cells[1]).innerHTML.toLowerCase();
      for (let j = 0; j < aQueries.length; j++) {
        let target = aQueries[j].toLowerCase();
        if (source.search(target) != -1) {
          oTable.tBodies[0].rows[i].cells[1].style.backgroundColor = 'yellow';
        }
      }
    }
  }
  my$('clear-color').onclick = alternateTableColor;
table,
th,
td {
  border: 1px solid black;
  border-collapse: collapse;
  text-align: center;
}

thead tr {
  font-weight: bold;
  border-bottom: 2px double black;
}

/* tbody > :nth-child(even){
            background-color: lightgrey;
        } */

table {
  width: 100%;
}

.data-input {
  margin-bottom: 10px;
  position: relative;
}

.data-input::after {
  content: "";
  clear: both;
  display: block;
}

.box {
  width: fit-content;
  margin: 20px auto;
}

.search {
  /* float: right; */
  margin-top: 10px;
}

#query {
  box-sizing: border-box;
  width: calc(100% - 106px);
}

#search-query,
#create-entry {
  box-sizing: border-box;
  margin-left: 2px;
  width: 100px;
}

.ul-input {
  border: none;
  border-bottom-width: 2px;
  border-bottom-style: inset;
  border-bottom-color: initial;
  padding-left: 5px;
}

.ul-input:focus {
  outline: none;
}

.ul-input::placeholder,
.ul-input {
  text-align: center;
}

.ul-input:focus::placeholder {
  color: transparent;
}
<div class="box">
  <div class="data-input">
    <div class="add-new">
      <label for="add-name">Name: </label><input type="text" id="add-name" class="ul-input" placeholder="Enter name" />
      <label for="add-age">Age: </label><input type="number" id="add-age" class="ul-input" placeholder="Enter age" />
      <input type="button" id="create-entry" value="Create" />
    </div>
    <div class="search">
      <input type="text" id="query" class="ul-input" placeholder="What are you looking for?" />
      <input type="button" id="search-query" value="Search" /><br>
      <input type="button" id="clear-color" value="Clear Color" />
    </div>
  </div>
  <table>
    <thead>
      <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Age</th>
        <th>Action</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>John</td>
        <td>30</td>
        <td><a href="javascript:void(0)">Delete</a></td>
      </tr>
      <tr>
        <td>2</td>
        <td>June</td>
        <td>40</td>
        <td><a href="javascript:void(0)">Delete</a></td>
      </tr>
    </tbody>
  </table>
</div>

2 Answers

You can call function your intentional position.

window.onload = function() {
    let oTable = document.getElementsByTagName('table')[0];
    window.alternateTableColor = function(){
        for (let i = 0; i < oTable.tBodies[0].rows.length; i++) {
            if(i % 2) {
                oTable.tBodies[0].rows[i].style.backgroundColor = 'lightgrey';
            } else {
                oTable.tBodies[0].rows[i].style.backgroundColor = '';
            }
        }
    }
    alternateTableColor(); // it works here

    let oBtn = document.getElementById('reset-color');
    oBtn.onclick = function(){;
        console.log('testing'); // console.log successfully
        alternateTableColor(); // you can call!!!!
    }
}

It's JavaScript's lexical scope. You can study this https://css-tricks.com/javascript-scope-closures/

Related