cancel
Showing results for 
Search instead for 
Did you mean: 
dmurphy1
Surveyor

jQuery Modal Dialog Box Not Rendering Consistently

Jump to solution

I have created a modal dialog box for the [institution's Canvas domain]/accounts/ page that provides a UI for selecting reporting options for Canvas API calls. My script can pull and process the data with no problem, but the dialog box does not render consistently on the page. Sometimes it works flawlessly with a gray overlay div behind the dialog. Sometimes the overlay div shows behind some of the page elements without the dialog box. Sometimes the overlay covers the dialog.

The user should be able to click the "Canvas API Reports" link that the script inserts below the Settings link. The reports link should open the dialog.

The script was written to run in Tampermonkey so the dialog and its form elements are dynamically added to the DOM.

The dialog rendered consistently until January of this year.

The portion of my code that applies to the dialog is inserted below.

/ ==UserScript==
// @name Canvas API Reports UI Test
// @namespace https://github.com/djm60546/canvas-api-reports
// @version 1.1
// @description Script for extracting student and instructor performance data using the Canvas API. Generates a .CSV download containing the data. Based on the Access Report Data script by James Jones.
// @author Dan Murphy, Northwestern University School of Professional Studies (dmurphy@northwestern.edu)
// @match https://northwestern.test.instructure.com/accounts/*
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.js
// @require https://code.jquery.com/jquery-3.4.1.js
// @require https://code.jquery.com/ui/1.12.1/jquery-ui.js
// @grant none

// ==/UserScript==

(function() {

'use strict';



function errorHandler(e) {
console.log(e.name + ': ' + e.message + 'at ' + e.stack);
alert('An error occured. See browser console for details.');
//wrapup();
}


function dateOptionsDlgResult(result) {
var dS, hS, dE, hE;
if (result) {
controls.rptDateStartTxt = $("#dm_start_date_txt").val();
controls.rptDateEndTxt = $("#dm_end_date_txt").val();
dS = Date.parse($("#dm_start_date_txt").val());
hS = dS.setHours(0,0,0,0);
controls.rptDateStart = hS;
dE = Date.parse($("#dm_end_date_txt").val());
hE = dE.setHours(24,0,0,0);
controls.rptDateEnd = hE;
$('#dm_date_range_p').html(controls.rptDateStartTxt + " - " + controls.rptDateEndTxt);
} else {
controls.rptDateStart = 0;
controls.rptDateEnd = 0;
controls.rptDateStartTxt = '';
controls.rptDateEndTxt = '';
$("#dm_rprt_prd_chbx").prop("checked",false);
$('#dm_date_range_p').html('');
}
}

function showDateOptionsDlg() {
try {
if ($('#dm_prtcptn_dts_dialog').length === 0) {
$('body').append('<div id="dm_prtcptn_dts_dialog"></div>');
$('#dm_prtcptn_dts_dialog').append('<p style="font-size: 1em">Select range of dates for zero participation.</p>');
$('#dm_prtcptn_dts_dialog').append('<form id="dm_prtcptn_dts_frm"></div>');
$('#dm_prtcptn_dts_frm').append('<fieldset id="dm_prtcptn_dts_fldst"></fieldset>');
$('#dm_prtcptn_dts_fldst').append('<label for="dm_start_date_txt">Start Date:</label>');
$('#dm_prtcptn_dts_fldst').append('<input type="text" id="dm_start_date_txt" name="dm_start_date_txt">');
$('#dm_prtcptn_dts_fldst').append('<label for="dm_end_date_txt">End Date:</label>');
$('#dm_prtcptn_dts_fldst').append('<input type="text" id="dm_end_date_txt" name="dm_end_date_txt">');
var enableDataPicker = $( function() {
$( "#dm_start_date_txt" ).datepicker();
$( "#dm_end_date_txt" ).datepicker();
} );
$('#dm_prtcptn_dts_dialog').dialog ({
'title' : 'Specify Reporting Period',
'autoOpen' : false,
buttons : {
"OK": function () {
var dS = Date.parse($("#dm_start_date_txt").val());
var dE = Date.parse($( "#dm_end_date_txt").val());
if (!(dS instanceof Date) || !(dE instanceof Date)) {
alert('Enter valid start and end dates for the reporting period')
return false;
} else if (dE < dS) {
alert('The end date must occur after the start date.');
return false;
} else {
$(this).dialog("close");
dateOptionsDlgResult(true);
}
},
"Cancel": function () {
$(this).dialog("close");
dateOptionsDlgResult(false);
}
}});
}
$('#dm_prtcptn_dts_dialog').dialog('open');
} catch (e) {
return false;
errorHandler(e);
}
}

function enableReportOptionsDlgOK() {
if (($("#dm_term_slct").val() != 0 || $("#dm_srch_inpt").val() != '') && $("#dm_report_slct").val() != 0) {
$('#dm_term_slct').closest(".ui-dialog").find("button:contains('OK')").removeAttr('disabled').removeClass( 'ui-state-disabled' );;
} else {
$('#dm_term_slct').closest(".ui-dialog").find("button:contains('OK')").prop("disabled", true).addClass("ui-state-disabled");
}
}

function reportOptionsDlg() {
try {
if ($('#dm_options_frm').length === 0) {
// Update this array with new Canvas term IDs and labels as quarters/terms are added
// Populates the term select menu in the "Select Report Options" dialog box
var terms = {data:[
{val : 0, txt: 'Select a term'},
{val : 166, txt: '2020 Summer'},
{val : 165, txt: '2020 Spring'},
{val : 164, txt: '2020 Winter'},
{val : 163, txt: '2019 Fall'},
{val : 129, txt: '2019-2020 Academic Year'},
{val : 131, txt: '2019-2020 Academic Year'},
{val : 128, txt: '2019 Summer'},
{val : 127, txt: '2019 Spring'},
{val : 124, txt: '2019 Winter'},
{val : 126, txt: '2018 Fall'},
{val : 125, txt: '2018-2019 Med Academic Year'},
{val : 130, txt: '2018-2019 Academic Year'},
{val : 123, txt: '2018 Summer'},
{val : 122, txt: '2018 Spring'},
{val : 121, txt: '2018 Winter'},
{val : 120, txt: '2017 Fall'},
{val : 118, txt: '2017-2018 Academic Year'},
{val : 119, txt: '2017 Summer'},
{val : 113, txt: '2017 Spring'},
{val : 112, txt: '2017 Winter'},
{val : 111, txt: '2016 Fall'},
{val : 109, txt: '2016-2017 Academic Year'},
{val : 110, txt: '2016 Summer'},
{val : 107, txt: '2016 Spring'},
{val : 106, txt: '2016 Winter'},
{val : 105, txt: '2015 Fall'},
{val : 108, txt: '2015-2016 Academic Year'},
{val : 103, txt: '2015 Summer'},
{val : 93, txt: '2015 Spring'},
{val : 96, txt: '2015 Winter'},
{val : 92, txt: '2014 Fall'},
{val : 104, txt: '2014-2015 Academic Year'},
{val : 115, txt: 'Advising Term'},
{val : 1, txt: 'Default Term'},
{val : 116, txt: 'Demo Term'},
{val : 114, txt: 'Prep Site Term'},
{val : 117, txt: 'Program Term'}
]};
// Populates the reports select menu in the "Select Report Options" dialog box
var reports = {data:[
{val : '0', txt: 'Select a report type'},
{val : 'at-risk', txt: 'At-risk Students'},
{val : 'access', txt: 'Course Resource Access'},
{val : 'instructor', txt: 'Instructor Presence'},
{val : 'participation', txt: 'Zero Participation'},
]};
// Define "Select Report Options" dialog box
$('body').append('<div id="dm_options_dialog"></div>');
$('#dm_options_dialog').append('<form id="dm_options_frm"></div>');
$('#dm_options_frm').append('<fieldset id="dm_options_fldst"></fieldset>');
$('#dm_options_fldst').append('<select id="dm_term_slct">');
$('#dm_options_fldst').append('<br/>');
$('#dm_options_fldst').append('<input type="radio" name="dm_srch_rdo" id="coursename" value="true" checked="checked">');
$('#dm_options_fldst').append('<label for="coursename"> Course Name</label>');
$('#dm_options_fldst').append('<br/>');
$('#dm_options_fldst').append('<input type="radio" name="dm_srch_rdo" id="instructorname" value="false">');
$('#dm_options_fldst').append('<label for="instructorname"> Instructor Name</label>');
$('#dm_options_fldst').append('<br/>');
$('#dm_options_fldst').append('<label for="dm_srch_inpt">Search text:</label>');
$('#dm_options_fldst').append('<input type="text" id="dm_srch_inpt" name="dm_srch_inpt">');
$('#dm_options_fldst').append('<hr/>');
$('#dm_options_fldst').append('<select id="dm_report_slct">');
$('#dm_options_fldst').append('<br/>');
$('#dm_options_fldst').append('<input type="radio" name="dm_report_opts" id="single" value="true" checked="checked">');
$('#dm_options_fldst').append('<label for="single"> Single report (all selected courses)</label>');
$('#dm_options_fldst').append('<br/>');
$('#dm_options_fldst').append('<input type="radio" name="dm_report_opts" id="multiple" value="false">');
$('#dm_options_fldst').append('<label for="multiple"> Multiple reports (individual courses)</label>');
$('#dm_options_fldst').append('<hr/>');
$('#dm_options_fldst').append('<input type="checkbox" id="dm_dl_crs_chbx" name="dm_dl_crs_chbx" value="true" checked>');
$('#dm_options_fldst').append('<label for="dm_dl_crs_chbx"> Online courses only</label>');
$('#dm_options_fldst').append('<br/>');
$('#dm_options_fldst').append('<input type="checkbox" id="dm_anon_stdnt_chbx" name="dm_anon_stdnt_chbx" value="true" checked>');
$('#dm_options_fldst').append('<label for="dm_anon_stdnt_chbx"> Anonymize students</label>');
$('#dm_options_fldst').append('<br/>');
$('#dm_options_fldst').append('<input type="checkbox" id="dm_rprt_prd_chbx" name="dm_rprt_prd_chbx" value="true">');
$('#dm_options_fldst').append('<label for="dm_rprt_prd_chbx"> Specify reporting period</label>');
$('#dm_options_fldst').append('<br/>');
$('#dm_options_fldst').append('<span id="dm_date_range_p" style="font-size: 1em; margin-left: 1.75em"></span>');
$("#dm_term_slct").change(function() {
enableReportOptionsDlgOK();
});
$("#dm_srch_inpt").change(function() {
enableReportOptionsDlgOK();
});
$("#dm_report_slct").change(function() {
if ($(this).children("option:selected").val() == 'participation')
{
$("#dm_anon_stdnt_chbx").prop("checked",false);
$("#dm_anon_stdnt_chbx").prop("disabled",true);
$("#dm_rprt_prd_chbx").prop("checked",true);
$("#dm_rprt_prd_chbx").prop("disabled",true);
showDateOptionsDlg();
}
else if ($(this).children("option:selected").val() == 'at-risk' || $(this).children("option:selected").val() == 'instructor')
{
$("#dm_anon_stdnt_chbx").prop("checked",false);
$("#dm_anon_stdnt_chbx").prop("disabled",true);
$("#dm_rprt_prd_chbx").prop("disabled",false);
}
else {
controls.rptDateStart = null;
controls.rptDateEnd = null;
$("#dm_anon_stdnt_chbx").prop("disabled",false);
$("#dm_rprt_prd_chbx").prop("disabled",false);
$("#dm_rprt_prd_chbx").prop("checked",false);
$('#dm_date_range_p').html('');
}
enableReportOptionsDlgOK();
});
$("#dm_rprt_prd_chbx").change(function() {
controls.rptDateStart = 0;
controls.rptDateEnd = 0;
if($(this).is(":checked"))
{
showDateOptionsDlg();
} else {
$('#dm_date_range_p').html('');
}
});
$('#dm_options_dialog').dialog ({
'title' : 'Select Report Options',
'autoOpen' : false,
'modal' : true,
'buttons' : {
"OK": function () {
$(this).dialog("close");
var enrllTrmSelct = $("#dm_term_slct option:selected").val();
var srchByChecked = $("input[name='dm_srch_rdo']:checked").val();
var srchTermsStr = $("#dm_srch_inpt").val();
controls.rptType = $('#dm_report_slct').children("option:selected").val();
controls.combinedRpt = $("input[name='dm_report_opts']:checked").val()
controls.dlCrsOnly = $('#dm_dl_crs_chbx').prop('checked');
controls.anonStdnts = $('#dm_anon_stdnt_chbx').prop('checked');
setupReports(enrllTrmSelct, srchByChecked, srchTermsStr);
},
"Cancel": function () {
$(this).dialog('close');
$('#jj_access_report').one('click', reportOptionsDlg);
}
}});
$('.ui-dialog-titlebar-close').remove(); // Remove titlebar close button forcing users to form buttons
$('#dm_term_slct').closest(".ui-dialog").find("button:contains('OK')").prop("disabled", true).addClass("ui-state-disabled");
if ($('#dm_term_slct').children('option').length === 0) { // add terms to terms select element
$.each(terms.data, function (key, value) {
$("#dm_term_slct").append($('<option>', {
value: value.val,
text: value.txt,
'data-mark': value.id
}));
});
}
if ($('#dm_report_slct').children('option').length === 0) { // add report types to reports select element
$.each(reports.data, function (key, value) {
$("#dm_report_slct").append($('<option>', {
value: value.val,
text: value.txt,
'data-mark': value.id
}));
});
}
}
//$('#dm_options_dialog').css('z-index', '9000');
$('#dm_options_dialog').dialog('open');
} catch (e) {
errorHandler(e);
}
}

// Add "Canvas API Reports" link below navigation
function addReportsLink() {
if ($('#jj_access_report').length === 0) {
$('#left-side').append('<div class="rs-margin-bottom"><a id="jj_access_report"><span aria-hidden="true" style="color:#f92626; cursor: pointer; display: block; font-size: 1rem; line-height: 20px; margin: 5px auto; padding: 8px 0px 8px 6px;">Custom API Reports</span><span class="screenreader-only">Custom API Reports</span></a></div>');
$('#jj_access_report').one('click', reportOptionsDlg);
}
return;
}

$( document ).ready(function() {
addReportsLink(); // Add reports link to page
});

}());‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Any insights or solutions will be appreciated.

Thanks,

Dan Murphy

1 Solution

Accepted Solutions
James
Navigator II

 @dmurphy1  

I am writing this without testing it, but I've coded around the issue in other places, so it would be where I start.

I see you're loading your own jQuery and jQuery UI without specifying the noConflict() method. This may cause it to clash with the version that Canvas is loading. A note on the noConflict page says "If for some reason two versions of jQuery are loaded (which is not recommended), calling $.noConflict( true ) from the second version will return the globally scoped jQuery variables to those of the first version." That means that you would to figure out the loading order. Your code may run before Canvas' jQuery is available, so their jQuery $ may override yours and your stuff doesn't work properly. Instead of using $, you should pick a different variable, like jq and use it throughout your code.

Probably unrelated to your modal issue, but I thought I'd mention it since there is a potential conflict, is the way you add the button to the menu. 

I see you've borrowed some code for adding the button in the addReportsLink() near the bottom. I'm flattered, but you should understand what the jj_access_report is for and adjust accordingly when you incorporate other people's code. I was trying to come up with an ID that would not ever conflict with ones used by Canvas. I figured they're unlikely to use my initials, so I went with jj_. The access_report was because that code was used for the access report script; I use other names depending on what the script is.

If you leave it like it is and try to load the access report script, it can cause problems since both are enabled on the page and IDs are supposed to be unique (although Canvas violates this rule themselves). Someone might reach out to me to figure out why my script isn't working anymore, not realizing that yours was using the same ID.

View solution in original post

2 Replies
James
Navigator II

 @dmurphy1  

I am writing this without testing it, but I've coded around the issue in other places, so it would be where I start.

I see you're loading your own jQuery and jQuery UI without specifying the noConflict() method. This may cause it to clash with the version that Canvas is loading. A note on the noConflict page says "If for some reason two versions of jQuery are loaded (which is not recommended), calling $.noConflict( true ) from the second version will return the globally scoped jQuery variables to those of the first version." That means that you would to figure out the loading order. Your code may run before Canvas' jQuery is available, so their jQuery $ may override yours and your stuff doesn't work properly. Instead of using $, you should pick a different variable, like jq and use it throughout your code.

Probably unrelated to your modal issue, but I thought I'd mention it since there is a potential conflict, is the way you add the button to the menu. 

I see you've borrowed some code for adding the button in the addReportsLink() near the bottom. I'm flattered, but you should understand what the jj_access_report is for and adjust accordingly when you incorporate other people's code. I was trying to come up with an ID that would not ever conflict with ones used by Canvas. I figured they're unlikely to use my initials, so I went with jj_. The access_report was because that code was used for the access report script; I use other names depending on what the script is.

If you leave it like it is and try to load the access report script, it can cause problems since both are enabled on the page and IDs are supposed to be unique (although Canvas violates this rule themselves). Someone might reach out to me to figure out why my script isn't working anymore, not realizing that yours was using the same ID.

View solution in original post

Hello James,

Thanks for your reply and advice. It worked!

Only the UI in my script is jQuery, the rest is vanilla JS. I inserted $.noConflict(true); after my UI code and that solved the rendering problem without impacting the Canvas code.

The code example I shared with you is a few weeks old. I have since replaced “jj_” in all element IDs.

You are right to be flattered (and deserve my thanks); your entire access report script (not just the UI) served as the basis of this project and the beginning of my efforts to make use of the Canvas LTI API.

- Dan