MDL-70032 qtype_multichoice: Fix answer labelling

* Discard the use of the label element in order to be able to render
multiple choice answers as they are and have these act as the radio
button/checkbox' label through the aria-labelledby attribute.
* New JS module qtype_multichoice/answers that listens for click events
on the answer text container and selects the appropriate answer radio
button/checkbox.
This commit is contained in:
Jun Pataleta 2020-10-29 15:36:15 +08:00
parent 99680d1be4
commit a1d81dc6d9
4 changed files with 73 additions and 8 deletions

View File

@ -0,0 +1,2 @@
define ("qtype_multichoice/answers",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b={ANSWER_LABEL:"[data-region=answer-label]"};a.default={init:function init(a){var c=document.getElementById(a),d=c.querySelectorAll(b.ANSWER_LABEL);d.forEach(function(a){a.addEventListener("click",function(a){var b=a.currentTarget.id,d=c.querySelector("[aria-labelledby=\"".concat(b,"\"]"));d.click()})})}};return a.default});
//# sourceMappingURL=answers.min.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["../src/answers.js"],"names":["SELECTORS","ANSWER_LABEL","init","rootId","root","document","getElementById","answerLabels","querySelectorAll","forEach","answerLabel","addEventListener","e","labelId","currentTarget","id","linkedOption","querySelector","click"],"mappings":"8IA6BMA,CAAAA,CAAS,CAAG,CACdC,YAAY,CAAE,4BADA,C,WAyBH,CACXC,IAAI,CAjBK,QAAPA,CAAAA,IAAO,CAACC,CAAD,CAAY,IACfC,CAAAA,CAAI,CAAGC,QAAQ,CAACC,cAAT,CAAwBH,CAAxB,CADQ,CAIfI,CAAY,CAAGH,CAAI,CAACI,gBAAL,CAAsBR,CAAS,CAACC,YAAhC,CAJA,CAKrBM,CAAY,CAACE,OAAb,CAAqB,SAACC,CAAD,CAAiB,CAClCA,CAAW,CAACC,gBAAZ,CAA6B,OAA7B,CAAsC,SAACC,CAAD,CAAO,IACnCC,CAAAA,CAAO,CAAGD,CAAC,CAACE,aAAF,CAAgBC,EADS,CAGnCC,CAAY,CAAGZ,CAAI,CAACa,aAAL,8BAAwCJ,CAAxC,QAHoB,CAKzCG,CAAY,CAACE,KAAb,EACH,CAND,CAOH,CARD,CASH,CAEc,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Handles events related to the multiple-choice question type answers.\n *\n * @module qtype_multichoice/answers\n * @package qtype_multichoice\n * @copyright 2020 Jun Pataleta <jun@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Selectors for this module.\n *\n * @type {{ANSWER_LABEL: string}}\n */\nconst SELECTORS = {\n ANSWER_LABEL: '[data-region=answer-label]',\n};\n\n/**\n * Init method.\n *\n * @param {string} rootId The ID of the question container.\n */\nconst init = (rootId) => {\n const root = document.getElementById(rootId);\n\n // Add click event handlers for the divs containing the answer since these cannot be enclosed in a label element.\n const answerLabels = root.querySelectorAll(SELECTORS.ANSWER_LABEL);\n answerLabels.forEach((answerLabel) => {\n answerLabel.addEventListener('click', (e) => {\n const labelId = e.currentTarget.id;\n // Fetch the answer this label is assigned to.\n const linkedOption = root.querySelector(`[aria-labelledby=\"${labelId}\"]`);\n // Trigger the click event.\n linkedOption.click();\n });\n });\n};\n\nexport default {\n init: init\n};\n"],"file":"answers.min.js"}

View File

@ -0,0 +1,57 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Handles events related to the multiple-choice question type answers.
*
* @module qtype_multichoice/answers
* @package qtype_multichoice
* @copyright 2020 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Selectors for this module.
*
* @type {{ANSWER_LABEL: string}}
*/
const SELECTORS = {
ANSWER_LABEL: '[data-region=answer-label]',
};
/**
* Init method.
*
* @param {string} rootId The ID of the question container.
*/
const init = (rootId) => {
const root = document.getElementById(rootId);
// Add click event handlers for the divs containing the answer since these cannot be enclosed in a label element.
const answerLabels = root.querySelectorAll(SELECTORS.ANSWER_LABEL);
answerLabels.forEach((answerLabel) => {
answerLabel.addEventListener('click', (e) => {
const labelId = e.currentTarget.id;
// Fetch the answer this label is assigned to.
const linkedOption = root.querySelector(`[aria-labelledby="${labelId}"]`);
// Trigger the click event.
linkedOption.click();
});
});
};
export default {
init: init
};

View File

@ -88,6 +88,7 @@ abstract class qtype_multichoice_renderer_base extends qtype_with_combined_feedb
$inputattributes['name'] = $this->get_input_name($qa, $value);
$inputattributes['value'] = $this->get_input_value($value);
$inputattributes['id'] = $this->get_input_id($qa, $value);
$inputattributes['aria-labelledby'] = $inputattributes['id'] . '_label';
$isselected = $question->is_choice_selected($response, $value);
if ($isselected) {
$inputattributes['checked'] = 'checked';
@ -102,15 +103,16 @@ abstract class qtype_multichoice_renderer_base extends qtype_with_combined_feedb
'value' => 0,
));
}
$questionnumber = html_writer::span($this->number_in_style($value, $question->answernumbering), 'answernumber');
$answertext = $question->format_text($ans->answer, $ans->answerformat, $qa, 'question', 'answer', $ansid);
$questionanswer = html_writer::div($answertext, 'flex-fill ml-1');
$radiobuttons[] = $hidden . html_writer::empty_tag('input', $inputattributes) .
html_writer::tag('label',
html_writer::span($this->number_in_style($value, $question->answernumbering), 'answernumber') .
html_writer::tag('div',
$question->format_text(
$ans->answer, $ans->answerformat,
$qa, 'question', 'answer', $ansid),
array('class' => 'flex-fill ml-1')),
array('for' => $inputattributes['id'], 'class' => 'd-flex w-100'));
html_writer::div($questionnumber . $questionanswer, 'd-flex w-100', [
'id' => $inputattributes['id'] . '_label',
'data-region' => 'answer-label',
]);
// Param $options->suppresschoicefeedback is a hack specific to the
// oumultiresponse question type. It would be good to refactor to
@ -151,6 +153,9 @@ abstract class qtype_multichoice_renderer_base extends qtype_with_combined_feedb
}
$result .= html_writer::end_tag('div'); // Answer.
// Load JS module for the question answers.
$this->page->requires->js_call_amd('qtype_multichoice/answers', 'init',
[$qa->get_outer_question_div_unique_id()]);
$result .= $this->after_choices($qa, $options);
$result .= html_writer::end_tag('div'); // Ablock.