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.