Scrolling Quizzes Made Easy

justin
Community Member
2
2947

Quick Start

  1. Make sure you have the Tampermonkey Chrome extension installed and activated.
  2. Download and install the scroll_quiz.js file from my github.

Longer Explanation

I have been using Canvas a long time and in several of my classes, I have quizzes (or more accurately "exams") that have dozens of questions, many with mixed question types (e.g. multiple-choice, true/false, matching, short answer, and essay). Canvas can automatically grade the multiple-choice, true/false and matching questions, but the short answer questions and essay questions are harder. For the short answer questions, I have tried to come up with all the possible answers I think students will write, but I always get a student who gives an answer that is correct but not EXACTLY the way I worded it, or they misspell their answer, which causes Canvas to mark it as incorrect. For the essay questions, those are never graded and I have to review them. This means that I am scrolling through the dozens of questions for EACH submission to find the ones that need review. Canvas provides a box at the top with links to the essay questions ONLY, but if I click on one of those links, then the box goes away and I have to scroll all the way back to the top of the quiz to access it. Very time-consuming and frankly annoying, am I right?

Links to essay questions ONLY . Once you click on one link, it scrolls and you have to scroll all the way back to the top in order to access the next link.Links to essay questions ONLY . Once you click on one link, it scrolls and you have to scroll all the way back to the top in order to access the next link.

Several years ago I read this post by James Jones about how to insert JavaScript into Canvas pages to allow things like sorting rubrics. Based on his script and some of my own research and expertise, I created a script that adds buttons to the Speed Grader that lets you jump to each question that is marked incorrect that is NOT a multiple-choice, true/false, or matching question. This has saved me a ton of time in grading my own quizzes so I want to share.

Tampermonkey

You can read more at James Jones' post, but this method involves installing a browser extension that manages "user scripts". The extension allows you to install scripts that can modify pages you are viewing in the browser it is installed in. The scripts have a line in them that tells the script to run only on URLs that match what is in the script, so it will only load the scripts for pages that match that URL pattern.

Tampermonkey is one of those "user script" extensions for Chrome. Greasemonkey is another one for Firefox. There are probably others, but those two are the most common.

This video shows how to install Tampermonkey:

Installing the script file

Once you have Tampermonkey installed, you can install the script file.

  1. Go to the script file on my github and copy the entire script file contents to your clipboard.
  2. Then click on the Tampemonkey icon near the top right corner of your Chrome window and choose "Create a new script...".
  3. In the text editor that opens up, select everything and then paste in the copied code from the script file.
  4. Click "File", then "Save" to save the script.

Editing the script file

The code should work on any Canvas installations that are hosted by Instructure, so if you are using Canvas through your university or school and the URL for canvas is something like "my_university.instructure.com" you don't need to edit anything else. If your university uses a different domain, you may need to edit the script file so it works with your domain.

At the top of the script you will see:

 

 

// ==UserScript==
// @Name        Scroll a Quiz
// @namespace   https://github.com/justinshewell/canvas
// @description This program allows the user to quickly scroll to quiz items that need review
// @include     https://*.instructure.com/courses/*/gradebook/speed_grader*
// @version     1
// @grant       none
// ==/UserScript==

 

 

To change the URL that the script works with, change "https://*.instructure.com/" to match your required domain name.

Script Explanation

Here is the entire script with an explanation:

 

 

// ==UserScript==
// @Name        Scroll a Quiz
// @namespace   https://github.com/justinshewell/canvas
// @description This program allows the user to quickly scroll to quiz items that need review
// @include     https://*.instructure.com/courses/*/gradebook/speed_grader*
// @version     1
// @grant       none
// ==/UserScript==
(function() {
  'use strict';

  const pageRegex = new RegExp('^/courses/[0-9]+/gradebook/speed_grader');
  if (!pageRegex.test(window.location.pathname)) {
    return;
  }

  var observer = new MutationObserver(function (mutations) {
      mutations.forEach(function (mutation) {
          [].filter.call(mutation.addedNodes, function (node) {
              return node.nodeName == 'IFRAME';
          }).forEach(function (node) {
              node.addEventListener('load', function (e) {
                  console.log('loaded: ' + node.src);
                  addScrollButtons();
              });
          });
      });
  });

  observer.observe(document.body, { childList: true, subtree: true });

  function addScrollButtons() {
      $.widget('ui.dialog', $.extend({}, $.ui.dialog.prototype, {
          _title: function(title) {
              var $title = this.options.title || ' '
              if( ('titleIsHtml' in this.options) && this.options.titleIsHtml == true )
                  title.html($title);
              else title.text($title);
          }
      }));
      $('#justin-buttons').remove();
      $('body').append('<div id="justin-buttons" data-qnum="-1"><small>Click the title bar above to drag this box anywhere on the screen.<br><br>These buttons scroll to questions that are marked incorrect or ungraded that are not multiple-choice, true/false, or matching questions, allowing you to easily review questions for grading without having to scroll yourself.</small><p style="text-align: center; margin-top: 20px;"><button id="justin-first" disabled>&lt;&lt; First</button> &nbsp; &nbsp; <button id="justin-previous" disabled>&lt; Previous</button> &nbsp; &nbsp; <button id="justin-next">Next &gt;</button> &nbsp; &nbsp; <button id="justin-last">Last &gt;&gt;</button></p></div>');
      $('#justin-buttons').dialog({
          height: 240,
          width: 450,
          title: 'Scroll Quiz Questions',
          titleIsHtml: true,
          resizable: true,
          draggable: true,
          dialogClass: 'alertDialog',
          modal: false,
          show: {
              effect: 'shake',
              duration: 1000
          }
      });
      $('#justin-first').on('click', function() {
          var iframe = $('#speedgrader_iframe');
          var curQuestion = 0;
          var top = $(iframe).contents().find('.question:not(.matching_question, .multiple_choice_question, .true_false_question, .correct)').first().offset().top;
          $(iframe).contents().children().animate({scrollTop: top}, 500);
          $('#justin-next, #justin-last').prop('disabled', false);
          $('#justin-previous, #justin-first').prop('disabled', true);
          $('#justin-buttons').attr('data-qnum', curQuestion);
      });
      $('#justin-next').on('click', function() {
          var iframe = $('#speedgrader_iframe');
          var curQuestion = $('#justin-buttons').attr('data-qnum');
          console.log(curQuestion);
          var els = $(iframe).contents().find('.question:not(.matching_question, .multiple_choice_question, .true_false_question, .correct)');
          console.log(els);
          curQuestion++;
          if(curQuestion >= els.length) return false;
          if(curQuestion > 0) $('#justin-first, #justin-previous').prop('disabled', false);
          if(curQuestion == els.length - 1) $('#justin-next, #justin-last').prop('disabled', true);
          var top = $(els[curQuestion]).offset().top;
          console.log(top);
          $(iframe).contents().children().animate({scrollTop: top}, 500);
          $('#justin-buttons').attr('data-qnum', curQuestion);
      });
      $('#justin-previous').on('click', function() {
          var iframe = $('#speedgrader_iframe');
          var curQuestion = $('#justin-buttons').attr('data-qnum');
          var els = $(iframe).contents().find('.question:not(.matching_question, .multiple_choice_question, .true_false_question, .correct)');
          curQuestion--;
          if(curQuestion < 0) return false;
          if(curQuestion == 0) $('#justin-first, #justin-previous').prop('disabled', true);
          if(curQuestion < els.length - 1) $('#justin-next, #justin-last').prop('disabled', false);
          var top = $(els[curQuestion]).offset().top;
          $(iframe).contents().children().animate({scrollTop: top}, 500);
          $('#justin-buttons').attr('data-qnum', curQuestion);
      });
      $('#justin-last').on('click', function() {
          var iframe = $('#speedgrader_iframe');
          var els = $(iframe).contents().find('.question:not(.matching_question, .multiple_choice_question, .true_false_question, .correct)');
          var curQuestion = $('#justin-buttons').attr('data-qnum');
          curQuestion = els.length - 1;
          var top = $(els[curQuestion]).offset().top;
          $(iframe).contents().children().animate({scrollTop: top}, 500);
          $('#justin-buttons').attr('data-qnum', curQuestion);
          $('#justin-next, #justin-last').prop('disabled', true);
          $('#justin-previous, #justin-first').prop('disabled', false);
      });
  }
})();

 

 

 

The first part, between the //==Userscript== lines is where the "user script" options are set. You should only need to change the domain as explained above.

The next few lines check the web address you are currently visiting to see if it matches the given pattern. If it doesn't, the script stops.

The next few lines set up an "observer" that looks to see if the Speed Grader content frame is loaded. If the frame is not loaded and we try to add features to the page, we will get errors, so we have to wait until the Speed Grader content frame is loaded. If the observer sees the frame is loaded, it runs the next part of the script.

The "addScrollButtons" function adds a "dialog" box with 4 buttons to the page:

Screen Shot 2021-12-12 at 11.23.28 AM.png

You can click and drag the dialog box wherever you want on the window. To jump to quiz questions, click on the appropriate buttons. When you finish grading one student's submission and move to the next one, the buttons reset.

Enjoy!

2 Comments