Cedi
Cedi

Reputation: 9

Why is the checked checkbox null?

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

Answers (2)

cgrom
cgrom

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

David Thomas
David Thomas

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>

JS Fiddle demo.

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>

JS Fiddle demo.

References:

Upvotes: 0

Related Questions