-1

I am trying to display a basic set and allow a user to add elements to the set. Here is what I have thus far:

<head>
    <script>
    class _Set extends Set {
        add(...args) { 
            for (const x of args) {
                this.add(x)
            }
            return this;
        }
    }
    const s = new Set();
    const button = document.querySelector('button');
    const text = document.getElementById('theText');
    button.addEventListener('click', () => {
        let values, rawValue = document.getElementById('theValue');
        if (rawValue.includes(',')) {
            values = rawValue.split(',').map(x => x.trim());
        } else {
            values = [rawValue,];
        }
        for (value of values) {
            s.add(value);
        }
        text.textContent = `Set(${s.size}) = ${Array.from(s)}`;
    });
    </script>
</head>

<body>
    <input type="text" id="theText" placeholder="Enter in a value"/>
    <input type="button" value="submit"/>
    Your set now contains: <span id="theSet">{}</span>
</body>

I suppose this error is occurring because the javascript tries running before the html dom has been rendered and is ready for scripting. What am I doing wrong here and what would be the correct way to do something such as the above?

David542
  • 104,438
  • 178
  • 489
  • 842

3 Answers3

0

You should put the script before the closing body tag:

<head>
    
</head>

<body>
    <input type="text" id="theText" placeholder="Enter in a value"/>
    <input type="button" value="submit"/>
    Your set now contains: <span id="theSet">{}</span>

    <script>
    class _Set extends Set {
        add(...args) { 
            for (const x of args) {
                this.add(x)
            }
            return this;
        }
    }
    const s = new Set();
    const button = document.querySelector('button');
    const text = document.getElementById('theSet');
    button.addEventListener('click', () => {
        let values, rawValue = document.getElementById('theValue');
        if (rawValue.includes(',')) {
            values = rawValue.split(',').map(x => x.trim());
        } else {
            values = [rawValue,];
        }
        for (value of values) {
            s.add(value);
        }
        text.textContent = `Set(${s.size}) = ${Array.from(s)}`;
    });
    </script>
</body>
Bryan
  • 141
  • 1
  • 9
0

There are two mistakes here.

First, you need to place your script at the end of the body, otherwise you will try to get elements not yet defined.

Second, the return of document.querySelector('button') is null. querySelector expects a valid CSS selector, but button is not. You could use input[type=button] as a valid selector in this case.

<body>
    <input type="text" id="theText" placeholder="Enter in a value"/>
    <input class="button" type="button" value="submit"/>
    Your set now contains: <span id="theSet">{}</span>

    <script>
    class _Set extends Set {
        add(...args) { 
            for (const x of args) {
                this.add(x)
            }
            return this;
        }
    }
    const s = new Set();
    const button = document.querySelector('input[type=button]');
    const text = document.getElementById('theSet');
    button.addEventListener('click', () => {
        let values, rawValue = document.getElementById('theValue');
        if (rawValue.contains(',')) {
            values = rawValue.split(',').map(x => x.strip());
        } else {
            values = [rawValue,];
        }
        for (value of values) {
            s.add(value);
        }
        text.textContent = `Set(${s.size}) = ${Array.from(s)}`;
    });
    </script>
</body>
volpato
  • 1,262
  • 17
  • 21
  • thanks, I get an error when running though -- I think related to my usage of `contains` and `strip` rather than `includes` and `trim`. I added my own answer that works btw. – David542 May 04 '22 at 22:05
-1

There are a lot of things here, but let's go through them, with the comments in-line

<body>
    <input type="text" id="theText" placeholder="Enter in a value"/>
    <input type="button" value="submit"/>
    <div>Your set now contains: <span id="theSet">{}</span></div>
    
    <script>
        // move the script to execute within the body of the 
        // html, after the needed nodes have loaded
        class _Set extends Set {
            add(...args) { 
                for (const x of args) {
                    super.add(x)
                }
                return this;
            }
            // moving the helper function to parse strings 
            // into a static method
            static getArrayFromInput(text) {
                let arr = [];
                if (text.includes(',')) {
                    arr = text.split(',').map(x => x.trim());
                } else {
                    arr = [text,];
                }
                return arr;
            }
        }
        const s = new _Set();
        // css selector needs to be on input[type=button]
        // and not input[type="button"] or button
        const button = document.querySelector('input[type=button]');
        const text = document.getElementById('theSet');

        // now we can add the event listener, 
        // since the button node has been fetched
        button.addEventListener('click', () => {
            // get the input[type=text] Node
            const rawValueNode = document.getElementById('theText');
            // pass the value contained in the input 
            // to the function the get the vallues as arr
            const arr = _Set.getArrayFromInput(rawValueNode.value);
            // add the values into the set
            s.add(...arr);
            // finally, format how we want to response to look in the html
            text.textContent = `Set(${s.size}) = ${Array.from(s)}`;
        });
    </script>
</body>
David542
  • 104,438
  • 178
  • 489
  • 842