├── README.md ├── css ├── form.css └── print.css ├── index.html ├── js ├── Option.js ├── Question.js ├── Quiz.js └── Result.js └── questions.js /README.md: -------------------------------------------------------------------------------- 1 | # javascript-quiz-library 2 | Very simple JS library for quiz creation 3 | 4 | ## Usage 5 | 6 | `git clone` and edit `questions.js`. See `index.html` to change the quiz name. 7 | 8 | ## Demo 9 | 10 | [See demo here.](https://zimmicz.github.io/javascript-quiz-library/) -------------------------------------------------------------------------------- /css/form.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #36393D; 3 | font-family: "Open Sans", Arial, sans-serif; 4 | font-size: 90%; 5 | margin: 1em auto; 6 | width: 750px; 7 | } 8 | 9 | fieldset { 10 | border-color: #356AA0; 11 | margin-bottom: 1em; 12 | 13 | } 14 | 15 | legend { 16 | font-size: 105%; 17 | font-weight: 600; 18 | padding-left: 15px; 19 | padding-right: 15px; 20 | padding-top: 15px; 21 | } 22 | 23 | label { 24 | display: block; 25 | line-height: 1.75em; 26 | } 27 | 28 | input[type="radio"] { 29 | margin-right: 10px; 30 | } 31 | 32 | input[type="submit"] { 33 | background: #689DD3; 34 | border: 1px solid #356AA0; 35 | color: white; 36 | display: block; 37 | font-size: 120%; 38 | font-weight: 600; 39 | height: 2.5em; 40 | margin-top: 2em; 41 | text-transform: uppercase; 42 | width: 100%; 43 | } 44 | 45 | table { 46 | color: white; 47 | font-weight: bold; 48 | margin: 1em auto 2em auto; 49 | width: 360px; 50 | } 51 | 52 | td { 53 | padding: 5px 15px; 54 | text-align: left; 55 | width: 60px; 56 | } 57 | 58 | td.missing-label, 59 | td.right-label, 60 | td.wrong-label { 61 | border-bottom: 1px solid; 62 | border-left: 1px solid; 63 | border-top: 1px solid; 64 | } 65 | 66 | td.missing-score, 67 | td.right-score, 68 | td.wrong-score { 69 | border-bottom: 1px solid; 70 | border-right: 1px solid; 71 | border-top: 1px solid; 72 | text-align: right; 73 | } 74 | 75 | td.missing-label, 76 | td.missing-score { 77 | background: #C79810; 78 | border-bottom-color: #946500; 79 | border-left-color: #946500; 80 | border-top-color: #946500; 81 | } 82 | 83 | td.right-label, 84 | td.right-score { 85 | background: #6BBA70; 86 | border-bottom-color: #38873D; 87 | border-top-color: #38873D; 88 | } 89 | 90 | td.wrong-label, 91 | td.wrong-score { 92 | background: #D01F3C; 93 | border-bottom-color: #9D0009; 94 | border-right-color: #9D0009; 95 | border-top-color: #9D0009; 96 | } 97 | -------------------------------------------------------------------------------- /css/print.css: -------------------------------------------------------------------------------- 1 | * { 2 | border-color: black; 3 | color: black; 4 | } 5 | 6 | fieldset { 7 | border-color: black; 8 | } 9 | 10 | input[type="radio"] { 11 | page-break-after: avoid; 12 | page-break-before: avoid; 13 | } 14 | 15 | input[type="submit"] { 16 | display: none; 17 | } 18 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JavaScript Quiz Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /js/Option.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {string} option value 5 | * @param {string} Question uid 6 | */ 7 | function Option(value, uid) { 8 | this.value = value; 9 | this.uid = uid; 10 | this.checked = false; 11 | } 12 | 13 | 14 | /** 15 | * Renders HTML. 16 | * @return {DOM Element} 17 | */ 18 | Option.prototype.render = function() { 19 | var label = document.createElement("label"), 20 | option = document.createElement("input"), 21 | self = this; 22 | 23 | option.value = this.value; 24 | option.type = "radio"; 25 | option.name = this.uid; 26 | 27 | option.addEventListener("change", function(e) { 28 | self.checked = this.checked; 29 | }); 30 | 31 | label.appendChild(option); 32 | label.appendChild(document.createTextNode(option.value)); 33 | 34 | return label; 35 | }; 36 | 37 | 38 | /** 39 | * @return {string} 40 | */ 41 | Option.prototype.getValue = function() { 42 | return this.value; 43 | }; 44 | 45 | 46 | /** 47 | * @return {boolean} 48 | * ugly 49 | */ 50 | Option.prototype.isSelected = function() { 51 | var choice = document.querySelector("input[name='" + this.uid + "']:checked"); 52 | 53 | return choice ? choice.value == this.value : false; 54 | }; 55 | -------------------------------------------------------------------------------- /js/Question.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {string} question to ask 5 | * @param {array} options to choose from 6 | * @param {integer} right option array index 7 | */ 8 | function Question(question, options, rightOption) { 9 | this.question = question; 10 | this.rightOption = rightOption; 11 | this.uid = this._uid(); 12 | this.options = this._createOptions(options); 13 | 14 | this.RNDR_RIGHT = "#6BBA70", 15 | this.RNDR_WRONG = "#D01F3C", 16 | this.RNDR_MISSING = "#C79810"; 17 | } 18 | 19 | 20 | /** 21 | * @return {string} question id 22 | */ 23 | Question.prototype._uid = function() { 24 | // https://stackoverflow.com/questions/3242324/javascript-dateobj-gettime-for-a-uid-is-the-length-not-fixed 25 | return "q" + Math.random().toString(36).substr(2,9); 26 | }; 27 | 28 | 29 | /** 30 | * @param {array} options 31 | * @return {array} of Option objects 32 | */ 33 | Question.prototype._createOptions = function(_options) { 34 | var options = []; 35 | 36 | for (var i = 0; i < _options.length; i += 1) { 37 | options[i] = new Option(_options[i], this.uid); 38 | } 39 | 40 | return options; 41 | } 42 | 43 | 44 | /** 45 | * @return {array} 46 | */ 47 | Question.prototype.getOptions = function() { 48 | return this.options; 49 | }; 50 | 51 | 52 | /** 53 | * @return {DOM Element} 54 | */ 55 | Question.prototype.render = function(shuffle) { 56 | var fieldset = document.createElement("fieldset"), 57 | legend = document.createElement("legend"), 58 | self = this; 59 | 60 | fieldset.id = this.uid; 61 | 62 | if (shuffle) { 63 | this._shuffleOptions(); 64 | } 65 | 66 | legend.innerHTML = this.question; 67 | 68 | fieldset.appendChild(legend); 69 | 70 | for (var i = 0; i < this.options.length; i += 1) { 71 | fieldset.appendChild(this.options[i].render()); 72 | } 73 | 74 | return fieldset; 75 | }; 76 | 77 | 78 | Question.prototype.renderMissing = function() { 79 | document.getElementById(this.uid).style.borderColor = this.RNDR_MISSING; 80 | }; 81 | 82 | 83 | Question.prototype.renderRight = function(first_argument) { 84 | document.getElementById(this.uid).style.borderColor = this.RNDR_RIGHT; 85 | }; 86 | 87 | 88 | Question.prototype.renderWrong = function() { 89 | document.getElementById(this.uid).style.borderColor = this.RNDR_WRONG; 90 | }; 91 | 92 | /** 93 | * @param {Option} 94 | * @return {boolean} 95 | */ 96 | Question.prototype.isRight = function(option) { 97 | return option.getValue() == this.options[this.rightOption].value; 98 | }; 99 | 100 | 101 | /** 102 | * Shuffles the options. 103 | */ 104 | Question.prototype._shuffleOptions = function() { 105 | var result = [], 106 | rightOption = this.options[this.rightOption].value; // obtain right option 107 | 108 | while (this.options.length) { 109 | var len = this.options.length, 110 | idx = parseInt(Math.random() * len); // find an index 111 | 112 | result.push(this.options.splice(idx, 1)[0]); // remove that item from array 113 | 114 | if (result[result.length - 1].value == rightOption) { 115 | this.rightOption = result.length - 1; // 116 | } 117 | } 118 | 119 | this.options = result; // switch arrays 120 | } 121 | -------------------------------------------------------------------------------- /js/Quiz.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | 4 | /** 5 | * Quiz 6 | * @param {string} name 7 | * @param {array} questions 8 | * @param {object} options 9 | * 10 | * options object supports these keys: 11 | * shuffle {boolean} should the questions be shuffled before rendered? 12 | */ 13 | function Quiz(name, questions, options) { 14 | this.name = name; 15 | this.questions = questions; 16 | this.options = options || {}; 17 | this.create(); 18 | } 19 | 20 | 21 | Quiz.prototype.getName = function() { 22 | return this.name; 23 | } 24 | 25 | 26 | Quiz.prototype.setName = function(name) { 27 | this.name = name; 28 | return this.name; 29 | }; 30 | 31 | 32 | Quiz.prototype.getQuestions = function() { 33 | return this.questions 34 | }; 35 | 36 | 37 | Quiz.prototype.setQuestions = function(questions) { 38 | this.questions = questions; 39 | return this.questions; 40 | }; 41 | 42 | 43 | Quiz.prototype.addQuestion = function(question) { 44 | return this.questions.push(question); 45 | }; 46 | 47 | 48 | Quiz.prototype.removeQuestion = function(question) { 49 | console.info("Remove question from the quiz"); 50 | return this.questions; 51 | }; 52 | 53 | 54 | Quiz.prototype.create = function() { 55 | var form = document.createElement("form"), 56 | submit = document.createElement("input"), 57 | self = this; 58 | 59 | document.title = this.name; 60 | document.write("

" + this.name + "

"); 61 | 62 | submit.type = "submit"; 63 | submit.value = "Submit quiz"; 64 | 65 | form.addEventListener("submit", function(e) { 66 | e.preventDefault(); 67 | self.submit(); 68 | }) 69 | 70 | for (var i in this.questions) { 71 | form.appendChild(this.questions[i].render(this.options.shuffle || false)); 72 | } 73 | 74 | form.appendChild(submit); 75 | document.body.appendChild(form); 76 | }; 77 | 78 | 79 | Quiz.prototype.submit = function() { 80 | var missing = 0, 81 | right = 0, 82 | wrong = 0; 83 | 84 | for (var q of this.getQuestions()) { 85 | var choice = null; 86 | 87 | // validate 88 | for (var o of q.getOptions()) { 89 | if (o.isSelected()) { 90 | choice = o; 91 | } 92 | } 93 | 94 | // evaluate 95 | if (!choice) { // skip evaluation 96 | q.renderMissing(); 97 | missing += 1; 98 | continue; 99 | } 100 | 101 | if (!q.isRight(choice)) { 102 | q.renderWrong(); 103 | wrong += 1; 104 | } else { 105 | q.renderRight(); 106 | right += 1; 107 | } 108 | } 109 | 110 | // show result 111 | var result = new Result(missing, right, wrong).render(); 112 | }; 113 | -------------------------------------------------------------------------------- /js/Result.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | 4 | /** 5 | * @param {integer} missing 6 | * @param {integer} right 7 | * @param {integer} wrong 8 | */ 9 | function Result(missing, right, wrong) { 10 | this.html = document.createElement("table"); 11 | 12 | var labels = ["Missing", "Right", "Wrong"], 13 | tr = document.createElement("tr"); 14 | 15 | for (var i = 0; i < arguments.length; i += 1) { 16 | var label = document.createElement("td"), 17 | score = document.createElement("td"); 18 | 19 | label.className = labels[i].toLowerCase() + "-label"; 20 | score.className = labels[i].toLowerCase() + "-score"; 21 | 22 | label.innerHTML = labels[i]; 23 | score.innerHTML = arguments[i]; 24 | 25 | tr.appendChild(label); 26 | tr.appendChild(score); 27 | } 28 | 29 | this.html.appendChild(tr); 30 | } 31 | 32 | 33 | Result.prototype.render = function() { 34 | var old = document.getElementsByTagName("table")[0]; 35 | if (old) { 36 | document.body.removeChild(old); 37 | } 38 | 39 | document.body.appendChild(this.html); 40 | 41 | return this; 42 | }; 43 | -------------------------------------------------------------------------------- /questions.js: -------------------------------------------------------------------------------- 1 | var questions = [ 2 | new Question("What is the capital of the Great Britain?", ["Bristol", "London", "Newcastle"], 1), 3 | new Question("What is the capital of the Czech Republic?", ["Prague", "Bratislava", "Vienna", "Wroclaw"], 0), 4 | new Question("What is the capital of Germany?", ["Berlin", "Budapest", "Bonn", "Hamburg"], 0), 5 | new Question("What is the biggest state in the USA?", ["Alaska", "Texas", "California", "Montana"], 0) 6 | ]; 7 | --------------------------------------------------------------------------------