简体   繁体   English

将下拉框更改为单选按钮以进行测验

[英]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:我所做的假设是:

  1. you want to convert the <option> elements to <input type="radio" /> elements, and你想将<option>元素转换为<input type="radio" />元素,并且
  2. you want to determine a total-score based on the value assigned to each of the <input> elements.您想要根据分配给每个<input>元素的值来确定总分。

With that in mind I've adjusted the HTML to better fit the identified assumptions and their requirements:考虑到这一点,我调整了 HTML 以更好地适应确定的假设及其要求:

  1. the value of each 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"
  2. the 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>元素都可以在视觉上识别为一个组,
  3. I've removed the number – which seems largely irrelevant – from the 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 作为数字是必需的要明确转义,
  4. I've also assigned a 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>

JS Fiddle demo . JS 小提琴演示

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:参考:

Bibliography:参考书目:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM