0

I'm struggling with the following issue: I must use basically just JS for creating a button.

//btn add
let button_add = document.createElement("button");
let button_add_class = button_add.className = "btn-add";
let button_add_click = button_add.setAttribute("onclick", "addBookToShoppingCart(first)");
let buttonText1 = document.createTextNode("Add to Shopping Cart");
button_add.appendChild(buttonText1);
this.mainBookScreen.appendChild(button_add);

The button need to call the addBookToShoppingCart(first) method, which is in the ShoppingCart class.

class ShoppingCart{
   addBookToShoppingCart(bookObject) {
         alert('Hello World!');
   }
}

If I click to the button I get the following error message in the browser's console:

Uncaught ReferenceError: addBookToShoppingCart is not defined
at HTMLButtonElement.onclick

Edit: My latest code is here:

class ShoppingCart{
    addBookToShoppingCart(bookObject) {
        alert('Hello World!');
    }
}

class Shop {
    constructor() {
        this.shoppingCart = new ShoppingCart("Shopping Cart");
        this.mainBookScreen = document.getElementById("mainBookScreen");
    }
    addBookToScreen(book) {
    //btn add
        const button_add = document.createElement("button");
        button_add.classList.add("btn-add");
        button_add.textContent = "Add to Shopping Cart";
        button_add.addEventListener("click", function(){this.shoppingCart.addBookToShoppingCart(first)});
        this.mainBookScreen.appendChild(button_add);
    }
}

class Book {
    //book constructors
}

let first = "TEST";
let book = [];
book[0] = new Book("bla1", "bla2", "bla3");
boook[1] = new Book("ble1", "ble2", "ble3");

const shop = new Shop();

shop.addBookToScreen(book[0]);
shop.addBookToScreen(book[1]);

Error message in browser if I click to the button:

Cannot read property 'addBookToShoppingCart' of undefined at HTMLButtonElement.

MartinB
  • 48
  • 6
  • 2
    because `addBookToShoppingCart` is not a global variable and you should NEVER use setAttribute to find events – epascarello Jun 08 '21 at 14:38
  • 1
    Does this answer your question? [How to call "class method" from outside class in JavaScript?](https://stackoverflow.com/questions/33837635/how-to-call-class-method-from-outside-class-in-javascript) – scrappedcola Jun 08 '21 at 14:39

3 Answers3

2

You've got a few problems.

First, don't set up events with DOM properties like onclick. While this can work, it's a more outdated approach that doesn't conform to the DOM Event Handling standard which is to use .addEventListener().

Next, shouldn't be setting up variables when you are simply setting the property of an object - - you can just set the property.

And, as for calling your method, you need to make an instance of your class before you can use it and when you do, you must call the properties and methods within the context of that instance.

class ShoppingCart{
   addBookToShoppingCart(bookObject) {
         alert('Hello World!');
   }
}

// Make an instance of the class:
let cart = new ShoppingCart();

let first = "TEST";

const button_add = document.createElement("button");

// You don't need variables to simply set properties:
button_add.classList.add("btn-add");
button_add.textContent = "Add to Shopping Cart";

// Use .addEventListener() to set up events, but the second argument takes
// a function reference, so your function invocation needs to be wrapped in 
// a reference, but within the invocation, you must call the function as
// a method of an instance of the class.
button_add.addEventListener("click", function(){cart.addBookToShoppingCart(first)});

document.body.appendChild(button_add);

Based on your edit, the problem is that within a DOM event this will refer to the DOM object that fired the event, not the class instance that the code is a part of. This is because of the closure that is created for the event handling code. If you replace this with the name of the instance, the code works as shown below:

class ShoppingCart{
    addBookToShoppingCart(bookObject) {
        alert('Hello World!');
    }
}

class Shop {
    constructor() {
        this.shoppingCart = new ShoppingCart("Shopping Cart");
        this.mainBookScreen = document.getElementById("mainBookScreen");
    }
    addBookToScreen(book) {
    //btn add
        const button_add = document.createElement("button");
        button_add.classList.add("btn-add");
        button_add.textContent = "Add to Shopping Cart";
        button_add.addEventListener("click", function(){
          // Can't use "this" to refer to the instance of the class
          // within a DOM event handler. Instead, refer to the instance
          // variable.
          shop.shoppingCart.addBookToShoppingCart(first);
        });
        this.mainBookScreen.appendChild(button_add);
    }
}

class Book {
    //book constructors
}

let first = "TEST";
let book = [];
book[0] = new Book("bla1", "bla2", "bla3");
book[1] = new Book("ble1", "ble2", "ble3");

const shop = new Shop();

shop.addBookToScreen(book[0]);
shop.addBookToScreen(book[1]);
<div id="mainBookScreen"></div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Thanks! You helped me a lot. And what if I have two classes. In this case `ShoppingCart` and `Shop`. I created a new ShoppingCart instance in constructor function within the Shop class. Besides the constructor function there is a `addBookToScreen(book)` method. I pasted here your code about `button_add` declaration and the `.addEventListener()` the same way as you explained. The `first` variable declaration is outside the classes. The error message if i click to the button: "shoppingCart.addBookToShoppingCart is not a function at HTMLButtonElement." What make I wrong? – MartinB Jun 08 '21 at 19:57
  • ...I found a previous topic [link](https://stackoverflow.com/questions/39175922/how-to-access-a-method-from-a-class-from-another-class) and I maked as you can see in the top rated answer (non-static) case. – MartinB Jun 08 '21 at 20:00
  • @MartinB We'd need to see your latest code. You should edit your question and append what you now have. – Scott Marcus Jun 08 '21 at 20:36
  • @ScottMarkus Edited and the latest code added. Thanks! – MartinB Jun 09 '21 at 05:50
  • @MartinB See my updated answer and don't forget to mark the answer as "the" answer by clicking the checkmark at the top, left of the answer. – Scott Marcus Jun 09 '21 at 12:52
0

The Class in JS has its own scope. In other words, the function addBookToShoppingCart is not available in the global scope, which means you can not access it without creating the object of the class. Below is an example of how to access the function inside the class

class Dog {
  function bark() {}
} 

var dog = new Dog();
dog.bark()

Please read about scopes and class in JS for more details.

kigibek
  • 601
  • 6
  • 3
0

I see 2 issues, one of this is the variable named boook, is undefined.

Another issue is in the function addEventListener, the this is referencing to the button, is like call Element.shoppingCart.addBookToShoppingCart(first).

For fix it, save a variable with this reference and use it, i will put you a complete example:


$(function(){

    class ShoppingCart{
        
        addBookToShoppingCart(bookObject) {
            alert('Hello World!');
        }
        
    }

    class Shop {
        constructor() {
            this.shoppingCart = new ShoppingCart();
            this.mainBookScreen = document.getElementById("mainBookScreen");
        }
        addBookToScreen(book){
        //btn add
            const button_add = document.createElement("button");
            button_add.classList.add("btn-add");
            button_add.textContent = "Add to Shopping Cart";
            
            let classhere = this;
            
            button_add.addEventListener("click", function(){
                
                //this -> button element..
                
                classhere.shoppingCart.addBookToShoppingCart(first);
                
            });
            
            this.mainBookScreen.appendChild(button_add);
        }
    }

    class Book {
        //book constructors
    }

    let first = "TEST";
    let book = [];
    book[0] = new Book("bla1", "bla2", "bla3");
    book[1] = new Book("ble1", "ble2", "ble3");

    const shop = new Shop();

    shop.addBookToScreen(book[0]);
    shop.addBookToScreen(book[1]);

});