Reputation: 9
Below it's a very simple javascript function. When I click the button it's supposed to show me the value of the checked checkbox but it't prints out an error in the console saying the checkedValue is null?
I tried looping through the checkboxes and getting the checked one and i get the same error. I would really appreciate some help!
<body>
<p id='txt'>here: </p>
<button id="btn" type="button" onclick="ok" >Click </button>
<input type="checkbox" class="ckb" value="1">one
<input type="checkbox" class="ckb" value="2">two
<input type="checkbox" class="ckb" value="3">three
<script>
var checkedValue = document.querySelectorAll('.ckb').checked;
document.getElementById('btn').addEventListener('click', function(){
document.getElementById('txt').innerText = checkedValue.value ;
});
</script>
</body>
Looping through the checkboxes
var checkedValue;
var inputElements = document.querySelectorAll('.ckb');
for(var i=0; i < inputElements.length; i++){
if(inputElements[i].checked===true){
checkedValue = inputElements[i];
break;
}
}
Upvotes: 0
Views: 2145
Reputation: 138
With some minor adjustments, this should be what you are looking for.
<body>
<p id='txt'>here: </p>
<button id="btn" type="button">Click </button>
<input type="checkbox" class="ckb" value="1">one
<input type="checkbox" class="ckb" value="2">two
<input type="checkbox" class="ckb" value="3">three
<script>
document.getElementById('btn').addEventListener('click', function()
{
var chkbxElements = document.getElementsByClassName("ckb");
for (element of chkbxElements)
{
if (element.checked)
{
document.getElementById('txt').innerText = `here: ${element.value}`;
}
}
});
</script>
</body>
Upvotes: 1
Reputation: 253308
To directly address your question, the problem lies in the code:
// this line returns a NodeList of all elements matching the
// supplied CSS selector ('.ckb'), this NodeList has no
// 'checked' property, and so will ultimately return
// undefined.
var checkedValue = document.querySelectorAll('.ckb').checked;
document.getElementById('btn').addEventListener('click', function(){
// here you try to access the 'value' property of the undefined
// Object returned earlier, which returns null:
document.getElementById('txt').innerText = checkedValue.value ;
});
As an alternative, I would suggest something like the following:
// using a const to declare the element, since it's unlikely
// you'll want to change which element triggers the function:
const button = document.getElementById('btn');
//here we bind the anonymous function of EventTarget.addEventListener()
// to the 'click' event upon that identified <button> element, using
// Arrow function syntax (since we don't require access to the
// 'this'):
button.addEventListener('click', (e) => {
// here we retrieve all elements which are checked (using the
// CSS pseudo-class ':checked') and have the class of 'ckb':
let checked = document.querySelectorAll('.ckb:checked'),
// retrieving the element into which the output should be
// displayed:
output = document.querySelector('#txt');
// here we update the text-content (using the textContent property)
// and set it equal to the results returned from:
// first converting the NodeList of checked
// into an Array, using Array.from(), using the
// Array.prototype.map() method to iterate over that
// Array:
output.textContent = Array.from(checked).map(
// returning the value of the element ('el'):
(el) => el.value
// joining those array elements together into a String, using
// Array.prototype.join(), and appending a period for
// grammatical correctness:
).join(', ') + '. ';
});
const button = document.getElementById('btn');
button.addEventListener('click', (e) => {
let checked = document.querySelectorAll('.ckb:checked'),
output = document.querySelector('#txt');
output.textContent = Array.from(checked).map(
(el) => el.value
).join(', ') + '. ';
});
*,
::before,
::after {
box-sizing: border-box;
font-size: 1rem;
line-height: 1.4;
margin: 0;
padding: 0;
}
#txt::before {
content: 'Checked items: '
}
#txt:empty::before {
color: #999;
content: 'No items checked.';
}
input {
margin-right: 0.5em;
}
<p id='txt'></p>
<button id="btn" type="button">Click</button>
<label>
<input type="checkbox" class="ckb" value="1">one
</label>
<label>
<input type="checkbox" class="ckb" value="2">two
</label>
<label>
<input type="checkbox" class="ckb" value="3">three
</label>
Note that I've also wrapped each <input>
element within a <label>
element, in order that clicking the text associated with each <input>
also toggles the checked state of that element.
Using CSS generated content, I've taken the liberty of giving an indication of the current state; when the <p id="txt">
element is empty (matching the :empty
pseudo-class, containing not even white-space) it shows the message "No items checked"), this may — or may not — represent a user-experience/interface improvement, but adjust to your own preference.
Further we move the event-binding out of the HTML mark-up, in order to reduce clutter in that mark-up, and reduce complications when it comes to future maintenance of the page.
You could, of course, bind the change
event on the <input>
elements themselves:
const inputs = document.querySelectorAll('.ckb'),
output = () => {
let results = document.querySelector('#txt'),
checked = Array.from(inputs).filter(
(el) => el.checked
);
results.textContent = checked.length === 0 ? '' : checked.map(
(el) => el.value
).join(', ') + '. ';
};
inputs.forEach(
(el) => el.addEventListener('change', output)
);
// getting a NodeList of all elements matching the supplied
// CSS selector:
const inputs = document.querySelectorAll('.ckb'),
// defining the function to bind as the event-
// handler, using Arrow function syntax:
output = () => {
// retrieving the element to which the results
// should be inserted:
let results = document.querySelector('#txt'),
// using Array.from() to convert the 'inputs'
// variable to an Array, and then calling
// Array.prototype.filter() to filter that
// Array returning a new one:
checked = Array.from(inputs).filter(
// 'el' refers to the current Array-element
// (node) of the Array we're iterating over,
// el.checked is a Boolean, and
// Array.protoype.filter() retains those Array-
// elements the assessment of which returns a
// true/truthy value (discarding those which do
// not):
(el) => el.checked
);
// here we use a ternary - conditional operator - to first
// check if the length of the checked Array is exactly zero;
// if so we return an empty string; otherwise we return
// the map of Array-element (node) values joined - as above -
// with a comma and space, with an appended period:
results.textContent = checked.length === 0 ? '' : checked.map(
(el) => el.value
).join(', ') + '. ';
};
// iterating over the NodeList of .ckb elements, with an
// Arrow function, and in each iteration we bind the
// output() function as the event-handler for the 'change'
// event:
inputs.forEach(
(el) => el.addEventListener('change', output)
);
*,
::before,
::after {
box-sizing: border-box;
font-size: 1rem;
line-height: 1.4;
margin: 0;
padding: 0;
}
#txt::before {
content: 'Checked items: ';
}
#txt:empty::before {
color: #999;
content: 'No items checked.';
}
<p id='txt'></p>
<label>
<input type="checkbox" class="ckb" value="1">one
</label>
<label>
<input type="checkbox" class="ckb" value="2">two
</label>
<label>
<input type="checkbox" class="ckb" value="3">three
</label>
References:
Array.from()
.Array.prototype.join()
.Array.prototype.map()
.document.getElementById()
.document.querySelector()
.document.querySelectorAll()
.Upvotes: 0