[LON-CAPA-cvs] cvs: loncom /interface loncommon.pm

raeburn raeburn at source.lon-capa.org
Sun Jun 28 08:07:51 EDT 2026


raeburn		Sun Jun 28 12:07:51 2026 EDT

  Modified files:              
    /loncom/interface	loncommon.pm 
  Log:
  - WCAG 2 compliance: keyboard-only access to modal windows.
    Focus is trapped within open modal window; use Esc key to exit.
  
  
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1531 loncom/interface/loncommon.pm:1.1532
--- loncom/interface/loncommon.pm:1.1531	Sun Jun 21 19:46:38 2026
+++ loncom/interface/loncommon.pm	Sun Jun 28 12:07:51 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1531 2026/06/21 19:46:38 raeburn Exp $
+# $Id: loncommon.pm,v 1.1532 2026/06/28 12:07:51 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -10959,6 +10959,21 @@
 <script type="text/javascript">
 // <![CDATA[
 // <!-- BEGIN LON-CAPA Internal
+
+function isSafari() {
+  var ua = navigator.userAgent.toLowerCase();
+  return ((ua.indexOf('safari') != -1) && (ua.indexOf('chrome') === -1));
+}
+
+function setIframeFocus(iframe) {
+  iframe.focus();
+  if(isSafari()) {
+    window.setTimeout(function() {
+      iframe.contentWindow.focus();
+    }, 100)
+  }
+}
+
 var modalWindow = {
 	parent:"body",
 	windowId:null,
@@ -10993,6 +11008,54 @@
 		modalWindow.height = height;
 		modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
 		modalWindow.open();
+                const iframe = document.querySelector('#myModal iframe');
+                if (iframe) {
+                    iframe.addEventListener('load', () => {
+                        const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
+                        const myFocusTrapper = (e) => {
+                            if (e.key === 'Escape') {
+                                modalWindow.close();
+                                return;
+                            }
+                            if (e.key === 'Tab') {
+                                const focusableSelector = [
+                                                           'iframe',
+                                                           '[href]:not([href="#"])',
+                                                           'input:not([disabled])',
+                                                           'select:not([disabled])',
+                                                           'textarea:not([disabled])',
+                                                           'button:not([disabled])',
+                                                           'object',
+                                                           'embed',
+                                                           '[contenteditable]',
+                                                           '[tabindex]:not([tabindex="-1"])'
+                                                          ].join(',');
+                                const rawElements = iframeDoc.querySelectorAll(focusableSelector);
+                                const iframeElements = Array.from(rawElements).filter(el => {
+                                    const style = iframe.contentWindow.getComputedStyle(el);
+                                    return style.display !== 'none' && style.visibility !== 'hidden';
+                                });
+                                if (iframeElements.length > 0) {
+                                    const lastidx = iframeElements.length - 1;
+                                    if (e.shiftKey) {
+                                        if (iframe.contentWindow.document.activeElement === iframeElements[0]) {
+                                            iframeElements[lastidx].focus();
+                                            e.preventDefault();
+                                        }
+                                    } else {
+                                        if (iframe.contentWindow.document.activeElement === iframeElements[lastidx]) {
+                                            iframeElements[0].focus();
+                                            e.preventDefault();
+                                        }
+                                    }
+                                }
+                            }
+                        };
+                        iframeDoc.body.addEventListener('keydown', myFocusTrapper);
+                        setIframeFocus(iframe);
+                    });
+                }
+ 
 	};
 // END LON-CAPA Internal -->
 // ]]>




More information about the LON-CAPA-cvs mailing list