1

I'm trying to re-write this demo in a OO way: https://www.w3schools.com/howto/howto_js_progressbar.asp This is my code:

document.getElementById("barButton").addEventListener("click", callMove);

function callMove(){
 var bar1 = new ProgressBar();
 bar1.move();
}

function ProgressBar() {
 this.elem = document.getElementById("myBar"),
 this.width = 1;
}

ProgressBar.prototype = {
 constructor: ProgressBar,
 move: function() {
  this.id = setInterval(this.frame, 300);
 },
 frame: function() {
  
  if(this.width >= 100) {
   clearInterval(this.id);
  }
  else {
   this.width++;
   if(this.width >= 50) {
    return;
   }
   this.elem.style.width = this.width + '%';
  }
 },
}
#myProgress {
  width: 100%;
  background-color: grey;
}

#myBar {
  width: 1%;
  height: 30px;
  background-color: black;
}
<html>

 <head>
  <title>
   This is a OO progress bar test.
  </title>
  <link rel="stylesheet" href="testOOProgressBar.css">
 </head>
 
 <body>
  <div id="myProgress">
   <div id="myBar"></div>
  </div>
  <br>
  <button id="barButton">Click Me</button> 
  <script src="testOOProgressBar.js"></script>
 </body>
 
</html>

Problem is, once I click the button, the bar does not progress as I expect, instead there is Uncaught TypeError: Cannot read property 'style' of undefined at frame in console. What's wrong here? It seems that this.width is not passed from Progressbar() to its prototype.

one-hand-octopus
  • 2,229
  • 1
  • 17
  • 51

1 Answers1

3

Your error means that you tried to read something like this:

undefined.style

By inspecting the code, you can see that the error comes from the Progressbar.frame function and it has only one line containing .style.

Then, see what's before it: this.elem... This is undefined!

The main problem is that:

setInterval sets this to the global object when running the provided function.

You can avoid that by .bind():

document.getElementById("barButton").addEventListener("click", callMove);

function callMove() {
  var bar1 = new ProgressBar();
  bar1.move();
}

function ProgressBar() {
  this.elem = document.getElementById("myBar"),
    this.width = 1;
}

ProgressBar.prototype = {
  constructor: ProgressBar,
  move: function() {
    this.id = setInterval(this.frame.bind(this), 300);
  },
  frame: function() {
    if (this.width >= 100) {
      clearInterval(this.id);
    } else {
      this.width++;
      if (this.width >= 50) {
        return;
      }
      this.elem.style.width = this.width + '%';
    }
  },
}
#myProgress {
  width: 100%;
  background-color: grey;
}

#myBar {
  width: 1%;
  height: 30px;
  background-color: black;
}
<html>

<head>
  <title>
    This is a OO progress bar test.
  </title>
  <link rel="stylesheet" href="testOOProgressBar.css">
</head>

<body>
  <div id="myProgress">
    <div id="myBar"></div>
  </div>
  <br>
  <button id="barButton">Click Me</button>
  <script src="testOOProgressBar.js"></script>
</body>

</html>
Community
  • 1
  • 1
FZs
  • 16,581
  • 13
  • 41
  • 50