To Our Amazing Educators Everywhere,
Happy Teacher Appreciation Week!
Found this content helpful? Log in or sign up to leave a like!
Hello Canvas developers,
I've recently run into an issue that I can't quite work out. To my knowledge (and in my experience) everything on the Canvas interface can be accessed via simple jQuery queries. This works across the board with every single element that I've wanted to manipulate so far, except the Next/Previous buttons:
I'm currently working on an extremely minimal interface for a specific course and I want to remove them and re-add them under certain circumstances. I usually achieve this by setting an element's display to none in CSS and then, by using jQuery and JS, re-display them if they match certain conditionals. For example:
This approach generally works fine. But when I try to re-add the Next button via the container class .module-sequence-footer-button--next through the query:
$(".module-sequence-footer-button--next").css("display", "inline-block");
... it doesn't work. The button can't be re-added but it can be removed in CSS via display:none without issue. I realise the specific class I'm referring to doesn't actually have a display value, but I've also tried with just the class .Button and the result is the same - but ONLY for elements in the footer div module-sequence-footer-content.
Can someone wise explain why everything within this div (module-sequence-footer-content) is impervious to jQuery queries and if there's an alternate way of editing them? 🙂
Thanks in advance.
Solved! Go to Solution.
In the end I solved the issue by making my own buttons. If anybody is interested in how this can be done, it's possible to make a GET request to the Canvas API (via new XMLHttpRequest in a JS file that you eventually upload to an account theme). The key API call is the "List module items" (find it here).
Parse the returned JSON data that contains the module items to JS (JSON.parse(data) is a built-in method of JS) and you end up with an array of objects - one object per module item ordered by their position in the module. Then, write a function to determine which module item is the current, open item in the browser window. To make a next button, it's now a simple matter of getting the html_url contained within the next item of the ModuleItem object array and passing that on to a button that's intended to send the user to the next module item.
If you're succesful, you now have a next button that you can display or not display using vanilla JS conditionals.
$(".module-sequence-footer-button--next").css("display");
reports it as
'block'
not "inline-block"
but when I try this in my browser console, it seems to work either way. This suggests to me that the issue might be React (which is a pain in the rear end to hack). You may need a mutation observer to appropriately trigger your code. $(document).ready often doesn't work with React-based pages.
Ah, React was on my suspect list as well. I got a feeling it's going to be easier to remove the Next/Previous buttons and insert two new home-made ones, that way I think I think I'll be able to control them.
Thanks for the help 🙂
In the end I solved the issue by making my own buttons. If anybody is interested in how this can be done, it's possible to make a GET request to the Canvas API (via new XMLHttpRequest in a JS file that you eventually upload to an account theme). The key API call is the "List module items" (find it here).
Parse the returned JSON data that contains the module items to JS (JSON.parse(data) is a built-in method of JS) and you end up with an array of objects - one object per module item ordered by their position in the module. Then, write a function to determine which module item is the current, open item in the browser window. To make a next button, it's now a simple matter of getting the html_url contained within the next item of the ModuleItem object array and passing that on to a button that's intended to send the user to the next module item.
If you're succesful, you now have a next button that you can display or not display using vanilla JS conditionals.
I'm glad you found a workaround that is working for you. I would suggest looking into the Get module item sequence API call to see if this can make your code a bit more efficient when creating your previous and next buttons. Based on the Canvas network traffic, it looks like it uses this API call to determine the previous and next module items for the previous/next buttons. This way you don't have to filter through the module items to find the appropriate ones.
Also, I haven't tested this yet myself, but I'm curious if the Document.readyState may help with knowing when all components are loaded. If you add an event listener to check for when the readyState is "complete", this may allow you to use and manipulate the native Canvas previous/next buttons rather than needing to make your own.
Hi James, huh - I missed that one when I was looking through the documentation. It looks promising, I'll respond back if I ever have more time to look at it. My script ended up a little verbose so I'd love to simplify it. Thanks! 🙂
Regarding the Document.readyState, I think it's the same state that jQuery references with $( document ).ready()? Not sure though. In any case, it might be worth experimenting with. I tried a few different solutions but was unable to find any reliable, overarching logic to the load order of the different elements. But when I was implementing my solution I discovered that the load order of all the elements wasn't totally consistent. In particular, the $( document ).ready(function()) came out buggy in certain regards, sometimes executing after user-made content had loaded (ie. the content of a content page) and sometimes before. Eventually, I used $(window).bind( "load", function() { } ) which delivered consistent results (at least for my script) so if I'd definitely recommend that if you or anyone else experience any problems regarding the load order - unless the state you mention delivers consistent results.
$(document ).ready() would correspond with a readyState of "interactive" or "complete". So, the page may still be in an "interactive" readyState meaning the DOM is loaded, but some content may still be loading based on sub-resources. This would explain the inconsistent experience with that.
However, as you already discovered, $(window).bind( "load", function() { } ) would correspond with a readyState of "complete" only since the window load event is only triggered after all content has loaded, including the sub-resources. https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event. Since you are already using the load event, it doesn't seem necessary to change to checking when the readyState is "complete" since it would have the same effect.
To participate in the Instructure Community, you need to sign up or log in:
Sign In