Disabling a dynamic form object issues

I'm trying to create a skill point window that takes an array of skills and gives it a displayed control in a grid format on a form with a plus and minus button on either side of it, I need to be able to disable/enable these buttons as the points I have to spend are allocated to each skill but I keep getting an error.

"TypeError: Cannot set properties of null (setting 'disabled')"

against this line

document.getElementById("SW-" + skill.name + "-Minus").disabled = false;

data.js

"use strict"

createItem("me", PLAYER(), {
    loc: "lounge",
    skillpoints: 4,
    intelligence: 1,
    strength: 1,
    stamina: 1,
    magic: 1,
  synonyms:['me', 'myself'],
  examine: "Just a regular guy.",
})

createRoom("lounge", {
  desc:"The lounge is boring, the author really needs to put stuff in it.",
})

createItem("SkillPoints", {
    alias: "Skill Points",
    loc: "lounge",
    hereVerbs: ["Interact"],
    examine: "A tool for upgrading your skills",
    interact: function () {
        msg("<a href=\"javascript:void(0);\" onclick=\"skillPointsWindow()\">Upgrade</a>")
    },
})

const skills = [
    {
        name: "INT",
        desc: "Intelligence",
        title: "Your character's intelligence level",
        currentValue: 0,
        maxValue: 0,
        skillIncrement: 1,
        skillPointsAdded: 0
    },
    {
        name: "STR",
        desc: "Strength",
        title: "Your character's strength level",
        currentValue: 0,
        maxValue: 100,
        skillIncrement: 1,
        skillPointsAdded: 0
    },
    {
        name: "STA",
        desc: "Stamina",
        title: "Your character's stamina level",
        currentValue: 0,
        maxValue: 100,
        skillIncrement: 1,
        skillPointsAdded: 0
    },
    {
        name: "MAG",
        desc: "Stamina",
        title: "Your character's stamina level",
        currentValue: 0,
        maxValue: 0,
        skillIncrement: 2,
        skillPointsAdded: 0
    }
]

var iAvailableSP
function skillPointsWindow() {

    const diag = document.querySelector("#dialogOKCancel")
    document.querySelector("#dialogOKCancel-title").innerHTML = "Skill Points"

    iAvailableSP = w.me.skillpoints

    console.log("Available Skill Points Before - " + iAvailableSP)

    let skillWindowHtml = '<div id="skill-window">'
    skillWindowHtml += '<table>'
    skillWindowHtml += '<tbody>'

    for (let skill of skills) {

        switch (skill.name) {
            case "INT":
                skill.currentValue = w.me.intelligence
                break;
            case "STR":
                skill.currentValue = w.me.strength
                break;
            case "STA":
                skill.currentValue = w.me.stamina
                break;
            case "MAG":
                skill.currentValue = w.me.stamina
                break;
        }

        skillWindowHtml += '<tr>'
        skillWindowHtml += '<td title="' + skill.title + '">' + skill.desc + ': </td><td><button type="button" id="SW-' + skill.name + '-Minus" onclick="SkillButtonOnClick(false, \'' + skill.name + '\')">-</button></td><td id="SW-' + skill.name + '-indicator">' + skill.currentValue + '</td><td><button type="button" id="SW-' + skill.name + '-Plus" onclick="SkillButtonOnClick(true, \'' + skill.name + '\')">+</button></td>'
        skillWindowHtml += '</tr>'

        if (iAvailableSP == 0) {
            // Read-only mode
            var btn = document.getElementById("SW-" + skill.name + "-Minus")
            btn.disabled = true;
            document.getElementById("SW-" + skill.name + "-Plus").disabled = true;
        }
        else {
            // Edit mode
            document.getElementById("SW-" + skill.name + "-Minus").disabled = false;
            document.getElementById("SW-" + skill.name + "-Plus").disabled = false;

            if (!skill.bNoMax & skill.currentValue == skill.maxValue) {
                document.getElementById("SW-" + skill.name + "-Plus").disabled = true;
            }
        }
        console.log("Skill " + skill.name + " Old Value - " + skill.currentValue)
    }
    skillWindowHtml += '</tbody>'
    skillWindowHtml += '</table>'
    skillWindowHtml += '</div>'

    document.querySelector("#dialogOKCancel-content").innerHTML = skillWindowHtml
    document.querySelector("#dialogOKCancel-OKButton").addEventListener('click', function () {
        // Update player skills from the skill window increases
        SkillWindowConfirmClick(diag)
    })
    document.querySelector("#dialogOKCancel-CancelButton").addEventListener('click', function () {
        SkillWindowCancelClick(diag);
    })
    diag.style.display = 'block'
    diag.style.width = 500 + 'px'
    diag.style.height = 'auto'
    diag.style.top = '100px'

    io.disable()
    diag.show()
    return true
}

function SkillWindowConfirmClick(diag) {

    // TODO Update actual Player Stats

    io.enable()
    document.querySelector('#textbox').focus()
    document.querySelector("#dialogOKCancel").style.display = 'none'
}

function SkillWindowCancelClick(diag) {
    io.enable()
    document.querySelector('#textbox').focus()
    document.querySelector("#dialogOKCancel").style.display = 'none'
}
function SkillButtonOnClick(bAdd, sSkill) {

    console.log("SP - " + iAvailableSP + " Adding? " + bAdd + " Skill - " + sSkill)

    var iSkillIndex = arrayObjectIndexOf(skills, sSkill, "name");

    if (iSkillIndex < 0) {
        // Something went wrong
        console.log("Skill - " + sSkill + " not found in skill list")
        return false
    }

    var skill = skills[iSkillIndex]

    console.log("Skill - " + skill.desc + " found in skill list")

    if (bAdd) {
        // Adding a skill point
            skill.skillPointsAdded++
            console.log("Skill point added for " + skill.desc)
            --iAvailableSP
    }
    else {
        // Subtracting a skill point
        if (skill.skillPointsAdded != 0) {
            // We can only decrement 1 point
            skill.skillPointsAdded--
            console.log("Skill point removed for " + skill.desc)
            
            ++iAvailableSP
        }
        else {
            // We cannot decrease the skill points as nothing has been allocated to it
            return true
        }
    }

    // Update the form
    document.getElementById("SW-" + skill.name + "-indicator").innerHTML = skill.currentValue - (skill.skillIncrement * skill.skillPointsAdded)

    // Update the array again
    skills[iSkillIndex] = skill

    console.log("Skill " + skills[iSkillIndex].name + " New Value - " + skills[iSkillIndex].currentValue)
    console.log("Available Skill Points After - " + iAvailableSP)
}

function arrayObjectIndexOf(myArray, searchTerm, property) {
    for (var i = 0, len = myArray.length; i < len; i++) {
        if (myArray[i][property] === searchTerm)
            return i;
    }
    return -1;
}

index.html addition

<dialog id="dialogOKCancel">
    <form method="dialog">
        <h4 id="dialogOKCancel-title"></h4>
        <hr />
        <div id="dialogOKCancel-content"></div>
        <div id="dialogOKCancel-footer" style="text-align:right">
            <hr />
            <button id="dialogOKCancel-OKButton" value="default">OK</button>
            <button id="dialogOKCancel-CancelButton" value="default">Cancel</button>
        </div>
    </form>
</dialog>

style.css addition

#dialogOKCancel {
    display: none;
    z-index: 10;
    position: fixed;
}

    #dialogOKCancel p {
        padding: 10px 0px 10px 0px;
        text-align: justify;
    }

You're building a string variable, skillWindowHtml, which contains a chunk of HTML.
In the middle of doing that, you attempt to enable or disable some of the controls.

The problem is that you're doing this before you actually add that string to the document. Those buttons don't exist yet.

My simplest guess for this would be:

    let skillWindowHtml = '<div id="skill-window">'
    skillWindowHtml += '<table>'
    skillWindowHtml += '<tbody>'

    for (let skill of skills) {

        switch (skill.name) {
            case "INT":
                skill.currentValue = w.me.intelligence
                break;
            case "STR":
                skill.currentValue = w.me.strength
                break;
            case "STA":
                skill.currentValue = w.me.stamina
                break;
            case "MAG":
                skill.currentValue = w.me.stamina
                break;
        }

        // we could do this inline, but I think it's easier to read this way
        let minusDisabledAttribute = (iAvailableSP == 0) ? 'disabled="disabled"' : '';
        let plusDisabledAttribute = (!skill.bNoMax & skill.currentValue == skill.maxValue) ? 'disabled="disabled"' : minusDisabledAttribute;

        skillWindowHtml += '<tr>'
        skillWindowHtml += '<td title="' + skill.title + '">' + skill.desc + ': </td><td><button type="button" id="SW-' + skill.name + '-Minus" onclick="SkillButtonOnClick(false, \'' + skill.name + '\')" ' + minusDisabledAttribute + '>-</button></td><td id="SW-' + skill.name + '-indicator">' + skill.currentValue + '</td><td><button type="button" id="SW-' + skill.name + '-Plus" onclick="SkillButtonOnClick(true, \'' + skill.name + '\') ' + plusDisabledAttribute + '">+</button></td>'
        skillWindowHtml += '</tr>'

        console.log("Skill " + skill.name + " Old Value - " + skill.currentValue)
    }
    skillWindowHtml += '</tbody>'
    skillWindowHtml += '</table>'
    skillWindowHtml += '</div>'

This adds the disabled attribute to the HTML string, rather than trying to change an attribute of an element which hasn't actually been created yet.



Support

Forums