0

Im building a todo list and I have a function that will remove a call and add one for the tabs to show active. I also have 3 divs one of which needs to be turned active when a tab is clicked. I have the tabs working but i can't get the div to at the same time. The function getList is removing the the correct class but adding another class to the nav and not the div. Im sure it has something to do with 'this' I've tried call and bind but no look. Im also trying to avoid Jquery and use vanilla JS. Any help in why this is pointing to the window and not my list?

edit: I've created a JS Fiddle also http://jsfiddle.net/sxuvvypp/embedded/result/

(function() {
  var elems = document.querySelectorAll("nav li");
  var listElems = document.querySelectorAll("div > ul");

  var makeActive = function() {
    for (var i = 0; i < elems.length; i++)
      elems[i].classList.remove('active');

    this.classList.add('active');
  };

  var getList = function() {
    for (var i = 0; i < listElems.length; i++)
      listElems[i].classList.remove('listActive');
    console.log(this);
    this.classList.add('listActive');
  }

  for (var i = 0; i < elems.length; i++)
    elems[i].addEventListener('click', function() {
      makeActive.call(this);
      getList(); //Issue here
    });
})();
body {
  margin: 0;
  padding: 0;
  background-color: darkgrey;
  font-family: Ariel, Charcoal, sans-serif;
}
#container {
  width: 500px;
  height: 525px;
  background-color: #f8f7f5;
  margin: 50px auto;
}
header {
  background: rgba(18, 136, 255, 1);
  background: -moz-linear-gradient(top, rgba(18, 136, 255, 1) 0%, rgba(8, 182, 245, 1) 100%);
  background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(18, 136, 255, 1)), color-stop(100%, rgba(8, 182, 245, 1)));
  background: -webkit-linear-gradient(top, rgba(18, 136, 255, 1) 0%, rgba(8, 182, 245, 1) 100%);
  background: -o-linear-gradient(top, rgba(18, 136, 255, 1) 0%, rgba(8, 182, 245, 1) 100%);
  background: -ms-linear-gradient(top, rgba(18, 136, 255, 1) 0%, rgba(8, 182, 245, 1) 100%);
  background: linear-gradient(to bottom, rgba(18, 136, 255, 1) 0%, rgba(8, 182, 245, 1) 100%);
  filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#b7deed', endColorstr='#08b6f5', GradientType=0);
  margin: 0;
  height: 60px;
  line-height: 60px;
  color: white;
  text-align: center;
}
nav {
  background-color: white;
}
nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  background-color: white;
  overflow: hidden;
}
nav li {
  float: left;
  width: 33.1%;
  border-right: 1px solid;
}
nav li:last-child {
  border: none;
}
nav li a {
  display: block;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
  color: black;
}
.active {
  background-color: #f8f7f5;
}
h1 {
  margin: 0;
}
.list {
  height: 250px;
  overflow: hidden;
  list-style: none;
  display: none;
}
.input {
  padding-left: 30px;
}
.listActive {
  display: block;
}
/*.yesterday{
 
}

.tomorrow{
 display: none;
}*/

input[type=checkbox] {
  display: none;
}
input[type=checkbox] + label:before {
  content: "";
  display: inline-block;
  width: 15px;
  height: 15px;
  vertical-align: middle;
  margin-right: 8px;
  background-color: #aaa;
  box-shadow: inset 0px 2px 2px rgba(0, 0, 0, .3);
  border-radius: 4px;
}
input[type=checkbox]:checked + label:before {
  content: "\2714";
  /* Tick */
  color: white;
  background-color: #666;
  text-align: center;
  line-height: 15px;
  text-shadow: 0px 0px 3px #eee;
}
.menu {
  border-top: 2px solid grey;
  width: 500px;
}
.menu label,
.menu input {
  display: block;
  width: 150px;
  float: left;
  margin: 10px 0px;
}
.menu label {
  text-align: right;
  padding-right: 20px;
  font-size: 20px;
}
br {
  clear: left;
}
<!DOCTYPE html>
<html>

<head>
  <title>To Do List</title>
  <link rel="stylesheet" type="text/css" href="style.css">
</head>

<body>
  <div id="container">
    <header>
      <h1>TO DO LIST</h1>
    </header>
    <nav>
      <ul>
        <li class="navButton"><a href="#">Yesterday</a></li>
        <li class="navButton active"><a href="#">Today</a></li>
        <li class="navButton"><a href="#">Tomorrow</a></li>
      </ul>
    </nav>
    <div class="yesterday">
      <ul class="list" id="yesterday">
        <li><input type="checkbox" id="cbox"><label for="cbox"></label>first</li>
      </ul>
    </div>
    <div class="today">
      <ul class="list listActive " id="list">
        <li><input type="checkbox" id="cbox"><label for="cbox"></label>second</li>
      </ul>
    </div>
    <div class="tomorrow">
      <ul class="list" id="tomorrow">
        <li><input type="checkbox" id="cbox"><label for="cbox"></label>third</li>
      </ul>
    </div>
    <div class="menu">
      <label>Add Task:</label>
      <input type="text" id="item">
      <br>
      <label>Date complete:</label>
      <input type="text" id="date">
      <br>
      <input type="button" value="Add" id="addButton">
      <input type="button" value="remove" id="removeButton">
    </div>

  </div>

  <script type="text/javascript" src="todo.js"></script>
</body>

</html>
  • Where is `classList` defined? `this` always refers to the context from which the function was called, so if you have a top-level function defined in javascript, `this` inside the function will be `window` since that's where the function is called. – Brennan Dec 14 '15 at 22:51
  • I think this can help you: http://web.archive.org/web/20110725013125/http://www.digital-web.com/articles/scope_in_javascript/ http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work – scareddragon Dec 14 '15 at 22:53
  • You might try the [MDN *this* article](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) (which needs updating for arrow functions). – RobG Dec 14 '15 at 23:24
  • @scareddragon thanks, everything i read relates to objects and the bits i read about functions all point to the window. – monkeyman905 Dec 15 '15 at 19:15

3 Answers3

1

Because of the way JavaScript scope works, a function that lives globally will naturally point to the window object. This is because, behind the scenes, a global function declaration looks like: window.getList.

A work around for your situation is passing in this as an argument for your getList function:

var getList = function (that){
    for (var i = 0; i < listElems.length; i++)
        listElems[i].classList.remove('listActive');
        that.classList.add('listActive'); //apply the passed in this value as `that`
}



for (var i = 0; i < elems.length; i++)
    elems[i].addEventListener('click', function(){
        makeActive.call(this);
        getList(this); //Pass in `this` value
});
dYale
  • 1,303
  • 1
  • 14
  • 19
  • It's better practice to use `bind` in this case: `var getList = function(){ this == context }.bind(context);` – Brennan Dec 14 '15 at 22:53
  • The value of a function's *this* has **nothing** to do with scope. It is set either by how the function is called, the use of *bind* or the outer execution context's *this* for arrow functions. – RobG Dec 14 '15 at 23:22
  • @Brennan—the use of "context" for *this* is discouraged. There is an *execution context* that has a particular *this* value. The two are distinctly different, though related, concepts. – RobG Dec 14 '15 at 23:36
  • @dYale sadly it doesn't seem to work 'that' points to
  • ​…​
  • ​ (todo.js, line 52) when i console.log it in the getList function and not the ul with the class 'list'. – monkeyman905 Dec 15 '15 at 18:33