I'd like to have a like/dislike hyperlink to display different contents on my page: when clicking 'like', displays 'good'; when clicking 'dislike', displays 'bad'. My code is like this:
<html>
<head>
<script>
function Homepage(){
this.like = document.getElementById("like");
this.dislike = document.getElementById("dislike");
Homepage.prototype = {
constructor: Homepage,
likeGame: function(event){
if(this.like.style.display == "none"){
this.like.style.display = "block";
}
event.preventDefault();
},
dislikeGame: function(event){
if(this.dislike.style.display == "none"){
this.dislike.style.display = "block";
}
event.preventDefault();
},
setListeners: function(){
console.log('in listen');
document.getElementById("hyperLike").addEventListener("click", this.likeGame);
document.getElementById("hyperDislike").addEventListener("click", this.dislikeGame);
}
}
}
</script>
</head>
<body>
<p style="display:block">
<a id="hyperLike" href="";>Like</a>/<a id="hyperDislike" href="";>Dislike</a> the game.
</p>
<p id="like" style="display:none">
good
</p>
<p id="dislike" style="display:none">
bad
</p>
<script>
var homepage = new Homepage();
window.onload = homepage.setListeners;
</script>
</body>
</html>
However it does not work. Clicking hyperlinks has no reaction. I added console.log
in setListeners
, nothing logged, so it does not even go into setListeners
. What's the problem here?
I have another none-OO version, which is basically same code, it works.
The problem is that this.like
inside the likeGame()
function is not the same as this.like
in the Homepage()
function, because a function has its own scope. One way to solve this is to use arrow functions as methods. Now this
will always refer to Homepage
.
function Homepage() {
this.like = document.getElementById("like");
this.dislike = document.getElementById("dislike");
this.likeGame = (event) => {
if (this.like.style.display == "none") {
this.dislike.style.display = "none"
this.like.style.display = "block";
}
event.preventDefault();
};
this.dislikeGame = (event) => {
if (this.dislike.style.display == "none") {
this.like.style.display = "none"
this.dislike.style.display = "block";
}
event.preventDefault();
};
this.setListeners = () => {
console.log('in listen');
document.getElementById("hyperLike").addEventListener("click", this.likeGame);
document.getElementById("hyperDislike").addEventListener("click", this.dislikeGame);
}
}
var homepage = new Homepage();
window.addEventListener("load", () => {
homepage.setListeners();
})
<html>
<body>
<p style="display:block">
<a id="hyperLike" href="">Like</a>/<a id="hyperDislike" href="" ;>Dislike</a> the game.
</p>
<p id="like" style="display:none">
good
</p>
<p id="dislike" style="display:none">
bad
</p>
</body>
</html>
The following demo can bind multiple elements to any applicable event and callback function. The advantage of using this class is that the parameters selector
, event
, and callback
can be anything and there's no extra steps to take for multiple listeners.
Demo
Details are commented in demo
This is a modification of the code featured in this article.
// Define class
class Homepage {
// Pass a CSS selector
constructor(selector) {
// Reference selector
const elements = document.querySelectorAll(selector);
// Get amount of elements
this.length = elements.length;
// Merge the constructor and elements
Object.assign(this, elements);
}
// Pass a callback function
each(callback) {
// Iterate elements...
for (let node of Array.from(this)) {
// ...call callback function fore each element...
callback.call(node);
}
// ...make the method chainable
return this;
}
// Pass event and callback
bind(event, callback) {
// Iterate constructor...
return this.each(function() {
// ...regiter each element to the event and assign callback
this.addEventListener(event, callback, false);
});
}
};
// Instantiate Homepage class
const likeHate = selector => new Homepage(selector);
// Callback rate()
const rate = e => {
// Reference clicked element
const tgt = e.target;
// If the clicked has .btn class...
if (tgt.matches('.btn')) {
// ...get the clicked value...
const val = e.target.value;
// ...reference article#rate...
const rate = document.getElementById('rate');
// ...assign the value of clicked to [data-rate] of #rate
rate.dataset.rate = val;
// If the value of clicked is 'Superior' = thumbs up/down
let icon = val === 'Superior' ? '