cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Collapse & Expand Modules

mcowen
Instructure
Instructure
33 9 5,612

For various reasons I decided to write a little CSS & JS to do the following:

  1. Load the modules page with all modules collapsed by default
  2. Create a button on the modules page to Expand/Collapse all modules
  3. When a user clicks on a link that takes them to a module in the modules page, make the modules page load with all modules collapsed EXCEPT the module that was clicked on the previous page.

This is what the page looks like when first opened. Notice the button at the top "Expand All Modules."

Screenshot of collapsed modules with button allowing to expand all modules

This is what the page looks like with the modules expanded. Notice the button at the top "Collapse All Modules."

Screenshot of expanded modules with button allowing to collapse all modules

I am putting the code here for others to use and improve.  If you make improvements, please share them with the rest of the community.  I am not a coding professional and consider myself to be an intermediate level, NOT an expert (although I got some help from some experts when I got stuck during this project Smiley Happy .)

If you have suggestions, please feel free to let me know. I'm always looking to improve my coding Smiley Happy

**DISCLAIMER**

This code is 100% unsupported by me and/or by Instructure. Use it at your own risk. Always try new code in your test or beta environments first. This will probably break. If you don't have someone on staff who can maintain it or fix it when it breaks, you will want to seriously reconsider using it in the first place.

Add this to the CSS file:

/*  Collapse all modules. 
     use this in combination with JS to auto-open the module link that was
     clicked to navigate to the page */


#content #context_modules .content,
#content #context_modules .collapse_module_link
{
  display: none;
}

#content #context_modules .expand_module_link {
  display: inline-block;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Add this to the JavaScript file:

//
// Use in combination with CSS that loads all modules in a collapsed state.
// When navigation to the modules page is to a specific module,
// automagically click the module so it opens in an expanded state.
//
var anchor = window.location.hash;
var underscore = anchor.indexOf("_") + 1;
var characters = anchor.length;
var     module = anchor.substring(underscore,characters);
var selector = "span[aria-controls='context_module_content_"+module+"']";
console.log(selector);

window.onload = function() {expand_module()};
function expand_module() {
    document.querySelector(selector).click();
     console.log("click attempted");
}

//
// Add button and script to expand and collapse all modules
//

// Create the button on Modules pages
if(window.location.href.indexOf("modules") > -1) {
     document.querySelector('.header-bar-right__buttons').insertAdjacentHTML( 'afterbegin', '<button class="btn" id="expand-collapse-modules" status="collapsed" onclick="expand_collapse_modules()"><span class="screenreader-only">Collapse or expand all modules</span>Expand All Modules</button>');
    }

// when the button is clicked, expand or collapse all modules that are not currently expanded or contracted.
function expand_collapse_modules()
{

    if (document.getElementById("expand-collapse-modules").status == "collapsed"){
         document.getElementById("expand-collapse-modules").innerText = "Expand All Modules";
         document.getElementById("expand-collapse-modules").status = "expanded";
          var items = document.querySelectorAll("span[style='display: inline-block;'][aria-expanded='true']");

         }
    else {        
         document.getElementById("expand-collapse-modules").innerText = "Collapse All Modules";
         document.getElementById("expand-collapse-modules").status = "collapsed";
          var items = document.querySelectorAll(".expand_module_link:not([style='display: none;'])");
         }
        
    for (var i = items.length-1; i >= 0 ; i--) {  
        console.log(i);
        items[i].click();
    }
        
} ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The code is also attached to this blog for your convenience.

9 Comments
irishb
Community Contributor

Hi Deactivated user‌!

Thanks so much for developing and sharing this lil' gem of functionality - we tested it last week and just added it to our college's production instance today, will let you know what our faculty and students think! : )

Best wishes,

Bridget Irish

Curricular Technology Support | Canvas Admin
The Evergreen State College, Olympia WA

Boekenoogen
Community Contributor

This is a nice bonus feature that Instructure should add to their code. Thanks for sharing this with the community.

mcowen
Instructure
Instructure

This is a common question I receive, so I thought I'd post it here...

Question:

Does this mean that the Modules page would load up quicker where there is a very long Modules list? I have a problem with very slow load times on a long Modules list currently and am looking for a solution?

Answer:

Yes. My intention when I wrote it was purely cosmetic, but an added benefit is faster load times Smiley Happy Yay!

As always, make sure to try any code in your TEST or BETA instance before using it in production.

dtod
Community Contributor

Latest release broke this for us. I think the issue is that the module content is loading asynchronously. Updated version below, which also remembers which modules are expanded and collapsed during a session - called by calling expand_module():

function showModule(obj){
     var relatedObj = obj.getAttribute('aria-controls');
     //console.log(relatedObj);
     //console.log(document.getElementById(relatedObj).style.display);
     if (document.getElementById(relatedObj).style.display == "" || document.getElementById(relatedObj).style.display == "none"){
          obj.click();
          // try again
          setTimeout(function(){ showModule(obj); },500);
     }
}

function expand_module() {
     var anchor = window.location.hash;
     var underscore = anchor.indexOf("_") + 1;
     var characters = anchor.length;
     var module = anchor.substring(underscore,characters);
     var thishref = window.location.pathname;
     if (module =="" && sessionStorage[thishref]){
          module = sessionStorage[thishref];
     }
     
     var modules=module.split(',');
     //console.log(module);
     
     if (module!=""){
          for (var x=0; x<modules.length; x++){
               var selector = "span[aria-controls='context_module_content_"+modules[x]+"'][aria-expanded='false']";
               //console.log(modules[x]);
               if (document.querySelector(selector) && modules[x]!=""){
                    //console.log('clicking ' + modules[x]);
                    //console.log(selector);
                    //console.log(document.querySelector(selector));
                    //document.querySelector(selector).click();
                    showModule(document.querySelector(selector));
               }
          }
     }
     else if (document.querySelectorAll("span[aria-controls^='context_module_content_']")[1]) {
          //console.log('default');
          //document.querySelectorAll("span[aria-controls^='context_module_content_']")[1].click();
          showModule(document.querySelectorAll("span[aria-controls^='context_module_content_']")[1]);
          sessionStorage[window.location.pathname]="," + document.querySelectorAll("span[aria-controls^='context_module_content_']")[1].parentNode.id;
     }
     
    //console.log("click attempted; now add button");
     
     //
     // Add button and script to expand and collapse all modules
     //

     // Create the button on Modules pages
     if(window.location.href.indexOf("modules") > -1 && document.querySelector('.header-bar-right__buttons') && !document.querySelector('#expand-collapse-modules')) {
           document.querySelector('.header-bar-right__buttons').insertAdjacentHTML( 'afterbegin', '<button class="btn" id="expand-collapse-modules" status="collapsed" onclick="expand_collapse_modules()"><span class="screenreader-only">Collapse or expand all modules</span>Expand All Modules</button>');
     }

     if (thishref.indexOf("modules") > -1){
          // capture expand and collapse ajax calls
          $(document).ajaxSuccess(function(event, request, settings) {
                 //console.log(settings);
                
                 if (/^\/courses\/\d+\/modules\/\d+\/collapse$/.test(settings.url) && settings.type=="POST"){
                    var urlComponents=settings.url.split('/');
                    module=urlComponents[urlComponents.length-2];
                    //console.log(module);
                    var expandedModules="";
                    var update=false;
                    if (sessionStorage[window.location.pathname]){
                         expandedModules=sessionStorage[window.location.pathname];
                    }
                    if (settings.data.indexOf('collapse=1')>-1){
                         //collapsed
                         update=true;
                         expandedModules = expandedModules.replace("," + module,"");
                    }
                    else if (module!="undefined" && expandedModules.indexOf("," + module)==-1){
                         //expanded
                         update=true;
                         expandedModules = expandedModules + "," + module;
                    }
                    if (update) sessionStorage[window.location.pathname]=expandedModules;
                 }
          });
     }
}

// when the button is clicked, expand or collapse all modules that are not currently expanded or contracted.
function expand_collapse_modules() {
    if (document.getElementById("expand-collapse-modules").status == "collapsed"){
         document.getElementById("expand-collapse-modules").innerText = "Expand All Modules";
         document.getElementById("expand-collapse-modules").status = "expanded";
          var items = document.querySelectorAll("span[style='display: inline-block;'][aria-expanded='true']");

         }
    else {        
         document.getElementById("expand-collapse-modules").innerText = "Collapse All Modules";
         document.getElementById("expand-collapse-modules").status = "collapsed";
          var items = document.querySelectorAll(".expand_module_link:not([style='display: none;'])");
         }
        
    for (var i = items.length-1; i >= 0 ; i--) {  
        // console.log(i);
        items[i].click();
    }
}

					
				
			
			
			
				
			
			
			
			
			
			
		
sharon_kitching
Community Participant

Just trying this out on BETA, and the button isn't generating. Any idea why? It's a fantastic idea Smiley Happy 

jwakeman
Community Member

 @dtod  Based on your reply, your revised code will automatically expand the module the user last had expanded - is that correct?  The code originally linked in the blog currently works for us, but your code does not generate the button.  We would love to have it remember the last module you were working in and have that expanded.

dtod
Community Contributor

Joe, that's correct. The way my code works it only functions on the modules page, not on a home page that has modules on it iirc.

jwakeman
Community Member

Yep, on the modules page we're not seeing the button generated via your code.  Also doesn't remember which modules you had left expanded previously when you browse away and back.

327032_Screen Shot 2019-10-18 at 10.28.36 AM.png

sgarcia1
Community Participant

A big THANK YOU to @mcowen @dtod and everyone who contributed to this. Just what my organization needed and works perfectly for me right now.

The only thing I can add is that the Collapse All / Expand all button is already implemented in the latest versions of Canvas, so there is no need for the second part of the Javascript code: "2. Create a button on the modules page to Expand/Collapse all modules".

No clue why this is not a native option in Canvas but again, thank you, guys.