Reputation: 1286
I'm trying to use JavaScript class architecture in combination with addEventListener
such that I can define a series of class methods once, which will be applied to all appropriate HTML and/or CSS objects. Below is an example with ONLY one circle, which should toggles red or blue when clicked. Unfortunately, as is, the class does architecture is unable to properly communicate with the DOM.
I've chosen to write the script in-line just to explore the basic concept more easily rather than juggling around multiple files for purposes of question cohesion (I do realize that in actuality, I'd likely use separate HTML and JS files.)
I'd like to code as is to be edited such that the circle changes color when clicked. (Also, the code DID function when class architecture was not used. I can add that if it's useful.)
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.circle {
height: 50px;
width: 50px;
background-color: #555;
border-radius: 50%;
}
</style>
</head>
<body>
<h2>Circle CSS</h2>
<div class="circle"></div>
<script>
class GameObject{
constructor(shape){
this.shape = shape;
this.clicks = 0;
};
clickEvent(){
this.shape.addEventListener('click',function(){
this.clicks += 1
if (this.clicks % 2 == 1){
this.shape.style.backgroundColor = 'red';
}else{
this.shape.style.backgroundColor = 'blue';
}
})
};
};
let shape = document.querySelector('.circle')
let s = new GameObject(shape);
console.log(s);
</script>
</body>
</html>
Also, the following questions/answers went above my head, though they are related: Javascript Class & EventListener Dealing with Scope in Object methods containing 'this' keyword called by Event Listeners
Edit: I took advice from comment and added clickEvent()
into this constructor. However, the following error was triggered:
Uncaught TypeError: Cannot read property 'style' of undefined
at HTMLDivElement.<anonymous> (circle.html:31)
Upvotes: 0
Views: 377
Reputation:
Change the name clickEvent
to handleEvent
, and move the .addEventListener
call outside the class.
Then instead of a function, pass your GameObject
instance as the event handler, and it'll work.
You don't event need to pass the element to the constructor that way. It's available from the event
object defined on the handleEvent
method.
class GameObject {
constructor() {
this.clicks = 0;
};
// Implement the EventListener interface
handleEvent(event) {
const shape = event.currentTarget;
this.clicks += 1
shape.style.backgroundColor = this.clicks % 2 == 1 ? 'red' : 'blue';
console.log(this); // `this` is your class instance
};
};
let g = new GameObject();
document.querySelector('.circle')
.addEventListener("click", g); // use the object as the handler
console.log(g);
.circle {
height: 50px;
width: 50px;
background-color: #555;
border-radius: 50%;
}
<h2>Circle CSS</h2>
<div class="circle"></div>
What's happening here is that by naming the method handleEvent
, you're causing your GameObject
class to implement the EventListener interface. Therefore instances of that class are eligible to be used as an event handler instead of a function.
Upvotes: 1
Reputation: 33726
clickEvent
.addEventListener
uses its own context, to solve this you can either declare a variable let that = this
and then use it inside of the handler, or you can use an arrow function in order to use the right context (instance of GameObject).This approach uses an arrow function
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.circle {
height: 50px;
width: 50px;
background-color: #555;
border-radius: 50%;
}
</style>
</head>
<body>
<h2>Circle CSS</h2>
<div class="circle"></div>
<script>
class GameObject{
constructor(shape){
this.shape = shape;
this.clicks = 0;
};
clickEvent(){
this.shape.addEventListener('click',() => {
this.clicks += 1
if (this.clicks % 2 == 1){
this.shape.style.backgroundColor = 'red';
}else{
this.shape.style.backgroundColor = 'blue';
}
})
};
};
let shape = document.querySelector('.circle')
let s = new GameObject(shape);
s.clickEvent();
console.log(s);
</script>
</body>
</html>
Upvotes: 0
Reputation: 3494
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.circle {
height: 50px;
width: 50px;
background-color: #555;
border-radius: 50%;
}
</style>
</head>
<body>
<h2>Circle CSS</h2>
<div class="circle"></div>
<script>
class GameObject{
constructor(shape){
this.shape = shape;
this.clicks = 0;
this.clickEvent(); // call the method to add the listener
};
clickEvent(){
this.shape.addEventListener('click',function(){
this.clicks += 1
if (this.clicks % 2 == 1){
this.shape.style.backgroundColor = 'red';
}else{
this.shape.style.backgroundColor = 'blue';
}
}.bind(this)) // bind callback function to the the correct 'this' context
};
};
let shape = document.querySelector('.circle')
let s = new GameObject(shape);
console.log(s);
</script>
</body>
</html>
Upvotes: 1