'Gender radio input and fields of interest checkbox undefined in alerts
Can anyone help me why the gender and fields of interest output are undefined? I can't seem to get it no matter how I get the value and its getting out of hand
HTML:
<form id="theForm" onsubmit="theSubmit()">
<div id="theGender">
<label for="theGender">Gender:</label>
<input type="radio" name="gender" value="Male"><a>Male</a>
<input type="radio" name="gender" value="Female"><a>Female</a>
<input type="radio" name="gender" value="Others"><a>Others</a>
<input type="radio" name="gender" value="Prefer not to say"><a>Prefer not to say</a>
</div>
<div id="fieldsOfInterest">
<label for="fieldsOfInterest">Fields Of Interest:</label>
<input type="checkbox" name="fieldsOfInterest" id="architectureEngineering" value="Architecture/Engineering">
<label for="fieldsOfInterest">Architecture/Engineering</label>
<input type="checkbox" name="fieldsOfInterest" id="computerScience" value="Computer Science">
<label for="fieldsOfInterest">Computer Science</label>
<label for="fieldsOfInterest">Environmental Sustainability</label>
<input type="checkbox" name="fieldsOfInterest" id="interestOther" value="Other">
<label for="fieldsOfInterest">Others</label>
</div>
<input type="submit" value="Submit" onclick="theSubmit()" name="submit">
<input type="reset" value="Reset" name="reset">
</form>
Script:
function theSubmit() {
if(theMale.checked){
theGender = "Male"
}else if(theFemale.checked){
theGender = "Female"
}else if(theOther.checked){
theGender = "Other"
}else if(thePreferNotToSay.checked){
theGender = "Prefer Not To Say"
}
if(architectureEngineering.checked){
fieldsOfInterest += "Architecture/Engineering"
}else if(computerScience.checked){
fieldsOfInterest += "Computer Science"
}else if(interestOther.checked){
fieldsOfInterest += "Others"
}
alert("This is the information that is filled out:\nName: "+firstName.value+" "+middleName.value+" "+lastName.value+"\nBirthday: "+birthDay.value+"\nGender: "+theGender.value+"\nCivil Status: "+civilStatus.value+"\nComplete Address: "+completeAddress.value+"\nContact Number: "+contactNumber.value+contactNumberInput.value+"\nFields Of Interest: "+fieldsOfInterest.value);
}
Solution 1:[1]
While one concern is that you've failed to initialise, or declare, your variables (theGender, architectureEngineering and so on) these variables are automatically available due to historic behaviour of Microsoft, in IE. They automatically created variables in the global scope with the variable-name equal to the element-id of any element in the document that had an id.
Presumably this was considered helpful at the time, but it automatically polluted the global scope and created variables that weren't always expected to be there, and which were – in many cases – overwritten.
However, while you haven't declared them – and arguably don't need to – you should; that way you and your programs know what to expect.
That said, there are other problems; hopefully the following code snippet demonstrates why you're receiving undefined when accessing the theGender; click the 'submit' button to show the sort-of explanation:
const sentenceContinuation = (node, hasCheckProp) => {
if (!hasCheckProp) {
return '';
} else if (node.checked) {
return ', and <em>is</em> checked';
} else if (!node.checked) {
return ', but is <em>not</em> checked';
}
return '';
};
function report(e) {
e.preventDefault();
const list = document.querySelector('ul');
list.innerHTML = '';
[...document.querySelectorAll('form [id]')].forEach(
(el) => {
let li = document.createElement('li'),
id = el.id,
elemType = el.tagName.toLowerCase(),
elHasChecked = el.matches('input:is([type=radio],[type=checkbox])'),
continuation = sentenceContinuation(el, elHasChecked),
article = ['a', 'e', 'i', 'o', 'u'].includes(elemType.charAt(0)) ? 'an' : 'a';
li.innerHTML = `<var>${id}</var> is ${article} <code><${elemType}></code>, and has ${elHasChecked ? 'a' : 'no'} <code>checked</code> attribute${continuation}; therefore <code>${id}.checked</code> returns <var>${el.checked}</code>.`;
list.append(li);
});
}
document.querySelector('form').addEventListener('submit', report);
*,
::before,
::after {
box-sizing: border-box;
font-family: system-ui, sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 1.4;
margin: 0;
padding: 0;
}
form,
ul {
margin-block: 1em;
margin-inline: auto;
width: 80vmax;
}
ul,
li {
list-style-type: none;
}
li {
margin-block: 0.5em;
}
var {
font-weight: 500;
font-style: normal;
}
var::before {
content: '"';
}
var::after {
content: '"';
}
code {
background-color: #aca9;
font-family: monospace;
padding-inline: 0.2em;
}
<form id="theForm">
<div id="theGender">
<label for="theGender">Gender:</label>
<input type="radio" name="gender" value="Male"><a>Male</a>
<input type="radio" name="gender" value="Female"><a>Female</a>
<input type="radio" name="gender" value="Others"><a>Others</a>
<input type="radio" name="gender" value="Prefer not to say"><a>Prefer not to say</a>
</div>
<div id="fieldsOfInterest">
<label for="fieldsOfInterest">Fields Of Interest:</label>
<input type="checkbox" name="fieldsOfInterest" id="architectureEngineering" value="Architecture/Engineering">
<label for="fieldsOfInterest">Architecture/Engineering</label>
<input type="checkbox" name="fieldsOfInterest" id="computerScience" value="Computer Science">
<label for="fieldsOfInterest">Computer Science</label>
<label for="fieldsOfInterest">Environmental Sustainability</label>
<input type="checkbox" name="fieldsOfInterest" id="interestOther" value="Other">
<label for="fieldsOfInterest">Others</label>
</div>
<input type="submit" value="Submit" name="submit">
<input type="reset" value="Reset" name="reset">
</form>
<ul></ul>
Incidentally, before we go further, the use of inline event-handlers (onsubmit, onchange, on...) is a little antiquated at this point, and for future ease-of-maintenance I'd strongly suggest that we instead use JavaScript to bind the relevant event-handling.
So, we need to:
- switch to JavaScript-enabled event-handling,
- declare the relevant variables we plan to use,
- ensure that the variables we try to use are fit for the purpose for which they're used.
In addition to this, in most online forms it's possible to interact with an <input> element by clicking it's adjacent text. This is a user-experience benefit, as it expands the area that can be clicked on to check, or uncheck, a check-box or radio <input>; so we'll use that as well. Further, there are better elements to use to group form-elements together and for identifying the purpose of that group, <fieldset>, and <legend>, elements.
// we declare submitHandler() function using Arrow syntax; and pass in a reference
// to the Event Object (passed automatically from EventTarget.addEventListener():)
const submitHandler = (evt) => {
// prevent the defautl action of the <form> element's submit event:
evt.preventDefault();
// caching a reference to the document, to reduce typing:
const D = document,
// getting a reference to the <form> element that initiated the event:
form = evt.currentTarget,
// from the <form> we find all <input> elements with a name-attribute
// equal to 'gender', and then wrap th at in an Array-literal with the
// spready syntax to create an Array from the NodeList returned by
// Element.querySelectorAll():
genderChoices = [...form.querySelectorAll('input[name=gender]')],
// we filter the Array of available gender options to retrieve only
// the checked option, using an arrow function (again), and testing
// to see if the element matches the ':checked' pseudo-class:
gender = genderChoices.filter((el) => el.matches(':checked')),
// we use the same Array-literal construction, along with
// Element.querySelectorAll() to retrieve an array of <input>
// elements with a name-attribute of "fieldsetOfInterest[]"
// which are checked:
interests = [...form.querySelectorAll('input[name="fieldsOfInterest[]"]:checked')],
// caching a reference to the element in which results will be displayed:
results = D.querySelector('.results'),
// testing to see if we have no chosen gender, which is true if the length
// of the gender Array is exactly equal to 0:
noGender = gender.length === 0,
// testing for chosen interests, as above:
noInterests = interests.length === 0,
// creating an <li> element:
li = D.createElement('li'),
// creating a document fragment:
fragment = D.createDocumentFragment();
// clearing any previous results from the 'results' Node:
results.innerHTML = '';
// we navigate from the first element of the Array of gender-choices
genderChoices[0]
// to its closest <fieldset>' ancestor Element:
.closest('fieldset')
// we access its classList property:
.classList
// and toggle the 'hasError' class; if noGender is true (and
// therefore no gender has been chosen by the user) this will
// add the class-name (if not already present); if the class-
// name is already present and noGender is false (which indicates
// the user has identified a chosen gender) the class-name will
// be removed:
.toggle('hasError', noGender);
// if noGender is false/falsey (so the user has chosen a gender):
if (!noGender) {
// we clone the <li>:
let genderClone = li.cloneNode();
// and use a template-literal to set its text-content to be equal to the
// value of the first Array-element of the gender Array:
genderClone.textContent = `Client has indicated their gender is: ${gender[0].value}.`
// we then append the genderClone <li> to the document-fragment:
fragment.append(genderClone);
}
// if noInterests is false/falsey (and so interests have been chosen):
if (!noInterests){
// we clone the <li>:
let interestClone = li.cloneNode();
// and we set its text-content with a template-literal; here we concatenate
// the JavaScript into the template-literal to form the String; we iterate
// over the intests Array, using Array.prototype.map():
interestClone.textContent = `Client has indicated an interest in: ${interests.map(
// with an Arrow function, returning the text-content of the current element's
// first associated <label>, and trimming that text-content using
// String.prototype.trim():
(choice) => choice.labels[0].textContent.trim()
// we then join that Array of strings together using Array.prototype.join():
).join(', ')}.`;
// then appending the <li> to the fragment:
fragment.append(interestClone);
}
// appending the fragment to the results <ul>:
results.append(fragment);
}
// finding the first <form> element in the document, and using
// EventTarget.addEventListener() to bind the submitHandler()
// function (note the deliberately ommitted brackets) as the
// event-handler for the 'submit' event:
document.querySelector('form').addEventListener('submit', submitHandler);
*,
::before,
::after {
box-sizing: border-box;
color: #363;
font-family: 'Montserrat', system-ui, sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 1.4;
margin: 0;
padding: 0;
}
form,
.results {
margin-block: 1em;
margin-inline: auto;
width: 90vmax;
}
form {
accent-color: #0f0;
}
fieldset {
padding: 0.5em;
}
legend {
margin-inline: 0.5em;
padding-inline: 0.5em;
}
legend::after,
span::after {
content: ':';
}
fieldset div {
display: flex;
flex-flow: row wrap;
gap: 0.5em;
justify-content: space-between;
}
label {
border: 1px solid #000;
border-radius: 0.5em;
cursor: pointer;
display: flex;
flex-flow: row-reverse nowrap;
flex-grow: 1;
justify-content: space-between;
padding-block: 0.25em;
padding-inline: 0.5em;
}
span {
transition: all 0.3s ease-in;
}
input:checked + span {
color: #393;
}
button {
padding-block: 0.25em;
padding-inline: 0.5em;
}
.hint {
flex-basis: 100%;
text-align: center;
transform: scaleY(0);
opacity: 0;
transition: all 0.3s ease-in;
}
fieldset.hasError .hint {
color: #933;
opacity: 1;
transform: scaleY(1);
}
.results:empty::before {
content: "No submitted results."
}
<!-- to import the Montserrat font (irrelevant, but I like the font) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
<form id="theForm">
<!-- we wrap the group of elements together using a <fieldset> element, which has
an (optional) <legend> child-element; that <legend> is used to identify the role
or purpose of the group of elements, here to identify the gender: -->
<fieldset>
<!-- used to identify the group's purpose/function: -->
<legend>Gender</legend>
<!-- I wanted to style, and position, the <label> elements with flex-box layout;
which the <fieldset> doesn't always seem to support, and I didn't want the
<legend> to be participate in the flex layout; so here we create a layout
context using a <div>: -->
<div>
<!-- the <input> is wrapped in a <label>, which implicitly links the <label>
to that <input>, you may notice that the <span> is after the <input> in
the DOM; but appears visually before the <input>; this is due to the
CSS (flex-flow) of the <label>, and is used for styling the associated
text of a :checked <input> -->
<label>
<input type="radio" name="gender" value="male">
<span>male</span>
</label>
<label>
<input type="radio" name="gender" value="female">
<span>female</span>
</label>
<label>
<input type="radio" name="gender" value="other">
<span>other</span>
</label>
<label>
<input type="radio" name="gender" value="unstated">
<span>prefer not to say</span>
</label>
<p class="hint">A choice must be made from the above options.</p>
</div>
</fieldset>
<fieldset>
<legend>Fields of Interest</legend>
<div>
<label>
<!-- for the purposes of submission to a back-end script, I've added
square-brackets to the name of these <input> elements, which
allows them to be submitted as an Array (this may not be required
but it can obviously be adjusted to fit your purposes): -->
<input type="checkbox" name="fieldsOfInterest[]" value="architecture-engineering">
<span>architecture/engineering</span>
</label>
<label>
<input type="checkbox" name="fieldsOfInterest[]" value="computer-science">
<span>computer science</span>
</label>
<label>
<input type="checkbox" name="fieldsOfInterest[]" value="environmental-sustainability">
<span>environmental sustainability</span>
</label>
</div>
</fieldset>
<fieldset>
<!-- for layout purposes the <button> elements are also wrapped in a <div> element,
and this group of controls has no associated <legend> since the 'submit' and
'reset' buttons indicate their function clearly enough: -->
<div>
<!-- I'm using <button> elements, instead of <input type="button">, because
a <button> maintains the button appearance, but can contain other elements
for purposes such as icons whereas an <input> has limited styling available: -->
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</div>
</fieldset>
</form>
<ul class="results"></ul>
References:
- Array-literals.
Array.prototype.filter().Array.prototype.forEach().Array.prototype.join().Array.prototype.map().- Arrow functions.
document.createDocumentFragment().document.createElement().document.querySelector().document.querySelectorAll().Element.append().Element.classListAPI.Element.closest().Element.innerHTML.Element.matches().Element.querySelector().Element.querySelectorAll().Event.EventTarget.addEventListener().Node.cloneNode().Node.textContent.- Spread (
...) syntax. String.prototype.trim().- Template-literals.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | David Thomas |
