[英]Changing Drop Down Boxes To Radio Buttons For Quiz
Code newbie here!代码新手在这里!
I've managed to put together a form which tallies up a user's score based on their selections.我设法整理了一个表格,根据用户的选择计算他们的分数。 And it works!它有效!
Here's my code:这是我的代码:
function finalScore(round) { var correct = 0; var selectValue; var questions = document.getElementsByClassName("question"); var numOfQuestions = questions.length; for (var i = 0; i < questions.length; i++) { //begin for loop //get the value of the select element selectValue = questions[i].options[questions[i].selectedIndex].value; //if the value equals right if (selectValue === "one") { //begin if then //increment the correct variable correct = correct + 1; } else if (selectValue === "two") { correct = correct + 2; } else if (selectValue === "three") { correct = correct + 3; } else if (selectValue === "four") { correct = correct + 4; } } //end for loop alert(correct); if (round === false) { //get the percentage of correct answers(not rounded) document.getElementById("scoreDisplay").innerHTML = correct; } else { //display the rounded value document.getElementById("scoreDisplay").innerHTML = correct; } //end if then else } //end function
How Old Are You? <select class="question" id="1howold"> <option value="three">19 - 26</option> <option value="four">27 - 36</option> <option value="two">37 - 40</option> <option value="one">41+</option> </select><br /><br /> What's Your Relationship Status? <select class="question" id="2relationshipstatus"> <option value="four">Single and excited to see what's out there.</option> <option value="three">Recently single and emotionally destroyed.</option> <option value="two">Got some casual things on the go, not looking for anything too serious.</option> <option value="one">In a committed relationship and only taking this quiz for the lols.</option> </select><br /><br /> Where Is Your Location? <select class="question" id="3location"> <option value="four">Location Independent</option> <option value="three">London/The UK</option> <option value="two">Europe</option> <option value="one">Elsewhere</option> </select><br /><br /> <button type="button" onclick="finalScore(true)">Submit</button> <div id="scoreDisplay">score goes here</div>
See the JSfiddle here: https://jsfiddle.net/tm3o5g0k/在此处查看 JSfiddle: https://jsfiddle.net/tm3o5g0k/
However, I've realised I need to switch these to radio buttons due to the length of some of the options.但是,我意识到由于某些选项的长度,我需要将它们切换为单选按钮。 I've tried so many methods and I keep making a mess?试了那么多方法还是老是乱七八糟? Surely there's an easy way to do this that I'm overlookng?当然有一种我忽略的简单方法可以做到这一点?
Thanks: :)谢谢: :)
Based on the code – and problem description – in your question, and comments to the question, I've developed the following solution.根据您问题中的代码和问题描述,以及对问题的评论,我开发了以下解决方案。 The assumptions I've made are:我所做的假设是:
<option>
elements to <input type="radio" />
elements, and你想将<option>
元素转换为<input type="radio" />
元素,并且<input>
elements.您想要根据分配给每个<input>
元素的值来确定总分。With that in mind I've adjusted the HTML to better fit the identified assumptions and their requirements:考虑到这一点,我调整了 HTML 以更好地适应确定的假设及其要求:
option
element is the numeric equivalent of the score, in order that they can be more easily used in calculations, therefore value="four"
has become value="1"
,每个option
元素的值都是score的等价数值,为了更方便的计算,所以value="four"
变成了value="1"
,textNode
prior to each of the <select>
elements has been converted to a <legend>
in order that each 'group' of <input>
elements can be visually recognised as a group,每个<select>
元素之前的textNode
已转换为<legend>
以便每个“组”的<input>
元素都可以在视觉上识别为一个组,id
of each <select>
, while under HTML 5 it's valid to have an id
starting with a numeral it's historically problematic to select such an id
via CSS as the number is required to be explicitly escaped,我已经从每个<select>
的id
中删除了这个数字——这似乎在很大程度上是不相关的,而在 HTML 5 下,有一个以数字开头的id
是有效的,它在历史上是有问题的 select 这样的id
通过 CSS 作为数字是必需的要明确转义,name
property to the <select>
elements, this is because the name
is required to correctly pass values to the server in the event that the results should be stored in a database, and also for <input type="radio">
elements to act as a mutually-exclusive group (so only one <input>
can be selected at a time from the related elements) a name
property must be assigned to identify that relationship.我还为<select>
元素分配了一个name
属性,这是因为如果结果应该存储在数据库中,并且对于<input type="radio">
,则需要name
才能正确地将值传递给服务器<input type="radio">
元素充当互斥组(因此一次只能从相关元素中选择一个<input>
)必须分配name
属性来标识该关系。 As such it made sense to add the name
to the <select>
prior to manipulation rather than relying on an auto-generated name
, which would, or could, lead to server-side storage problems.因此,在操作之前将name
添加到<select>
而不是依赖自动生成的name
是有意义的,这会或可能会导致服务器端存储问题。With all that said, one approach to solving your problems is below:尽管如此,解决问题的一种方法如下:
// declaring the functions as constants, using 'const'; // variables declared with const cannot be reassigned, // though if the variable is an Object or Array the // properties, or contents, of that Object or Array can // be changed/updated later: const selectToRadio = (selectSelector) => { // within the function body we retrieve all relevant elements // using document.querySelectorAll(), passing the CSS selector // supplied to the function; then that iterable static // NodeList is converted to an Array using the spread syntax: const selects = [...document.querySelectorAll(selectSelector)], // we create basic elements: input = document.createElement('input'), label = document.createElement('label'), span = document.createElement('span'); // set the type of the created <input> element: input.type = 'radio'; // here we take the Array of Nodes and iterate over that // Array using Array.prototype.forEach() with an Arrow // function: selects.forEach( // 'sel' is a reference to the current <select> element // from the Array of <select> elements over which we're // iterating: (sel) => { // here we assign the property-value of the 'name' property // of the <select> element as the 'groupName' of the <inpu> // elements we're creating: let groupName = sel.name, // here we retrieve all descendant <option> elements // from the current <select> element, using // Element.querySelectorAll() and, again, creating // an Array - with the spread operator - of the // resulting iterable NodeList: options = [...sel.querySelectorAll('option')], // here we create a documentFragment() so all the // created elements can be appended at the same time: fragment = document.createDocumentFragment(); // here we again use Array.prototype.forEach() to iterate over // the Array of <option> elements: options.forEach( // 'opt' (these variables can be named whatever you like, so // so long as it's a valid variable-name within the naming // rules of JavaScript) is a reference to the current <option> // of the Array of <option> elements over which we're // iterating: (opt) => { // here we create clones of the various elements we // created earlier (rather than creating the same // type of element multiple times in each iteration // of the loop): let inputClone = input.cloneNode(), labelClone = label.cloneNode(), spanClone = span.cloneNode(); // here we set the 'name' and 'value' properties of the // cloned <input>: inputClone.name = groupName; inputClone.value = opt.value; // we use the Element.classList API to add the 'answer' // class-name to the created element: inputClone.classList.add('answer'); // we set the textContent of the created <span> to be // equal to the text from the current <option>: spanClone.textContent = opt.text; // we use the parentNode.append() method to append the // cloned <input> and the cloned <span> elements to // the cloned <label>: labelClone.append(inputClone, spanClone); // then we append the cloned <label> to the documentFragment: fragment.append(labelClone); }); // we use parentNode.insertBefore() method to insert the // documentFragment into the DOM before the current <select> // element: sel.parentNode.insertBefore(fragment, sel); // and then use childNode.remove() method to remove // the current <select> element - and its descendants - // from the DOM: sel.remove(); }); }, score = () => { // here we use the same approach as above to create an Array of // element-nodes that have the 'answer' class-name and are also checked: const answers = [...document.querySelectorAll('.answer:checked')], // here we take the answers Array, and then call: result = answers // Array.prototype.map() to create a new Array based on the // contents of the current Array; here that new Array is // an Array of Numbers: .map( // we again use Arrow syntax for the anonymous function, // here 'el' is a reference to the current checked <input> // element; the function body (the single line following // the '=>' characters) simply returns the value of the // checked <option> parsed as a Number in base 10. Any // attribute-value (from which the value property is // derived is returned as a String from JavaScript, which // is why we're parsing it to a Number): (el) => parseInt(el.value, 10)) // we then pass the Array of Numbers to the // Array.prototype.reduce() function in order to reduce // the chained Array to a single result; again we use an // Arrow function syntax; here the body of the function // is taking the previously-held (or default) value ('a') and // adding it to the value of the current Array-element (b); // the '0' at the end of the function is the default-value with // which the reduce function starts: .reduce((a, b) => a + b, 0); // we then find the element that will show the result, and update its // text-content to be equal to the resulting score: document.querySelector('#scoreDisplay').textContent = result; // here we return the result to the calling context in the event you want // to also store or otherwise manipulate the derived score: return result; }; // here we call the selectToRadio() function, passing in an // appropriate CSS selector: selectToRadio('select'); // here we retrieve the first or only <button> element in the document: document.querySelector('button') // and use EventTarget.addEventListener() to bind the named score() // function (note the deliberate lack of parentheses) as the // event-handler for the 'click' event: .addEventListener('click', score);
fieldset { margin: 0.5em 0; } legend { padding: 0 0.6em; } label { display: flex; gap: 0 0.6em; margin: 0.3em 0; } input+span { border-radius: 0.6em; } input:checked+span { color: limegreen; }
<form action=""> <fieldset> <legend>How old are you?</legend> <select class="question" id="howold" name="ageRange"> <option value="3">19 - 26</option> <option value="4">27 - 36</option> <option value="2">37 - 40</option> <option value="1">41+</option> </select> </fieldset> <fieldset> <legend>What's your relationship status?</legend> <select class="question" id="relationshipstatus" name="relationshipStatus"> <option value="4">Single and excited to see what's out there.</option> <option value="3">Recently single and emotionally destroyed.</option> <option value="2">Got some casual things on the go, not looking for anything too serious.</option> <option value="1">In a committed relationship and only taking this quiz for the lols.</option> </select> </fieldset> <fieldset> <legend>What is your location?</legend> <select class="question" id="location" name="location"> <option value="4">Location Independent</option> <option value="3">London/The UK</option> <option value="2">Europe</option> <option value="1">Elsewhere</option> </select> </fieldset> <button type="button">Submit</button> </form> <div id="scoreDisplay"></div>
In the above code we used the following approach:在上面的代码中,我们使用了以下方法:
[...document.querySelectorAll('CSS-selector')]
to efficiently create Arrays from the NodeList result of the document.querySelectorAll()
, this is unnecessary in the selectToRadio()
function as we only use Array.prototype.forEach()
which could be replaced with NodeList.prototype.forEach()
with no changes to the syntax.为了从document.querySelectorAll()
的 NodeList 结果中有效地创建 Arrays,这在selectToRadio()
function 中是不必要的,因为我们只使用Array.prototype.forEach()
可以用NodeList.prototype.forEach()
代替,没有语法的变化。
However in the composition of the functions I hadn't entirely decided how I was going to implement the code in advance, so I converted the NodeList
s to Arrays simply in order that I might be able to use other Array methods – such as Array.prototype.map()
, Array.prototype.filter()
– as I did in the score()
function.然而在函数的组合上我还没有完全决定我将如何提前实现代码,所以我将NodeList
s 转换为 Arrays 只是为了我可以使用其他 Array 方法 - 例如Array.prototype.map()
, Array.prototype.filter()
– 正如我在score()
function 中所做的那样。
I don't believe it will increase efficiency by much, if at all, but if you'd rather not waste processing time converting a NodeList
into an Array then simply convert the following lines:我不相信它会提高很多效率,如果有的话,但如果你不想浪费处理时间将NodeList
转换为 Array 那么只需转换以下行:
const selects = [...document.querySelectorAll(selectSelector)],
and:和:
options = [...sel.querySelectorAll('option')],
into:进入:
const selects = document.querySelectorAll(selectSelector),
and:和:
options = sel.querySelectorAll('option'),
other than that no changes need to be made and when, for example:除此之外,不需要进行任何更改,例如:
selects.forEach(
(sel) => ....
is called the JavaScript engine will use NodeList.prototype.forEach()
automatically in place of Array.prototype.forEach()
(proof of concept: JS Fiddle demo ).称为 JavaScript 的引擎将自动使用NodeList.prototype.forEach()
代替Array.prototype.forEach()
(概念证明: JS Fiddle 演示)。
In the score()
function, however, I do use the other methods of the Array.prototype
which are not translated to, or available on, NodeLists so that must remain an Array.然而,在score()
function 中,我确实使用了Array.prototype
的其他方法,这些方法未转换为 NodeLists 或在 NodeLists 上不可用,因此必须保留为 Array。
References:参考:
Array.prototype.filter()
. Array.prototype.filter()
。Array.prototype.forEach()
. Array.prototype.forEach()
。Array.prototype.map()
. Array.prototype.map()
。Array.prototype.reduce()
. Array.prototype.reduce()
。ChildNode.remove()
. ChildNode.remove()
。const
. const
。document.createDocumentFragment()
. document.createDocumentFragment()
。document.createElement()
. document.createElement()
。document.querySelector()
. document.querySelector()
。document.querySelectorAll()
. document.querySelectorAll()
。Element.classList
API . 元素类列表Element.classList
。Element.querySelectorAll()
. Element.querySelectorAll()
。EventTarget.addEventListener()
. EventTarget.addEventListener()
。HTMLOptionElement
. HTMLOptionElement
。Node.cloneNode()
. Node.cloneNode()
。Node.insertBefore()
. Node.insertBefore()
。Node.textContent
. Node.textContent
。NodeList.prototype.forEach()
. NodeList.prototype.forEach()
。ParentNode.append()
. ParentNode.append()
。Bibliography:参考书目:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.