Like/dislike hyperlink does not work in an Object-

2019-08-24 08:24发布

问题:

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.

回答1:

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>



回答2:

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' ? '