0

I'm using JavaScript fetch API to generate random quote. on page load it change but when I click on the new quote button it give type error .

onclick on the button =

TypeError: Cannot read properties of undefined (reading 'length') at HTMLButtonElement.newQuote

link to JS Bin

HTML Markup

<div id="quote-container">
    <div>
      <span id="quote"></span>
    </div>
    <div>
      <span id="author"></span>
    </div>
    <div>
      <button id="newQuote">New Quote</button>
    </div>
  </div>

JavaScript code

`

class Quote {

    apiQuote = [];
    apiUrl = '';
    
    constructor(apiUrl){
        this.apiUrl = apiUrl;
    };
    
    newQuote(){
        
        const quoteTxt = document.getElementById('quote');
        const author = document.getElementById('author');
        
            
       const quote = this.apiQuote[Math.floor(Math.random() * this.apiQuote.length)]; 
        
        quoteTxt.innerHTML = quote.text;
        author.innerHTML = quote.author;
            
    };
    
    
    async getQuote(){
        try{
            let spread = this.apiUrl;
            
            let response = await fetch(this.apiUrl);
            this.apiQuote = await response.json();
            
            this.newQuote();
        }catch(e){
            console.log(e);
        }
    }

}

let quote = new Quote('https://type.fit/api/quotes');

quote.getQuote();

document.getElementById('newQuote').addEventListener('click',quote.newQuote)

`

  • You must bind the function - `quote.newQuote.bind(quote)` – kelsny Nov 03 '22 at 18:55
  • thank you it worked! @caTS . will it be necessary to bind all event Listener when using class? – Quobi Slim Nov 03 '22 at 19:07
  • If you pass it directly, yes. – kelsny Nov 03 '22 at 19:07
  • @QuobiSlim Only those that depend on a `this` context. For classes, specifically, there’s an alternative: `newQuote = () => {`…`}` instead of `newQuote(){`…`}`. Arrow functions don’t assume their own `this` binding, so `this` refers to the same thing immediately outside the function. Since that’s you class body, it’ll always be the instance `quote` (or whichever instance you use to call `newQuote` with). – Sebastian Simon Nov 03 '22 at 19:27

1 Answers1

1

The this whose apiQuote property you are accessing in document.getElementById('newQuote').addEventListener('click',quote.newQuote) is the event target button. If you want to reference the Quote instance, you should change the event listener callback to either a properly bound function (e.g. ()=>quote.newQuote() or quote.newQuote.bind(quote)) or, since you seem to be interested in always referencing the instance's apiQuote, you can permanently bind the newQuote method in the Quote constructor:

    constructor(apiUrl){
        this.apiUrl = apiUrl;
        this.newQuote = this.newQuote.bind(this);
    }
kelsny
  • 23,009
  • 3
  • 19
  • 48
Jared A Sutton
  • 369
  • 3
  • 8