Collapse & Expand Modules

Instructure
Instructure
33 8 3,281

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.

8 Comments
Community Member

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

Community Member

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

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.

Surveyor

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();
    }
}

					
				
			
			
			
				
			
			
			
			
			
			
		
Learner II

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

Community Member

dtod@vccs.edu 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.

Surveyor

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.

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

About the Author
Mike has 20 years of experience in public education as a Teacher, Professional Development Specialist, Instructional Coach, and Director of Technology. He works closely with universities and k-12 school districts in student-centered change management. Mike has worked hands-on with hundreds of institutions with implementation and adoption strategies ranging from technical SIS integrations to vision and goal planning, professional development plans and course design. He also likes to play around with JavaScript, CSS and the Canvas API.
Labels