Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/** * Scroll cue for [[Template:Sticky table start]] tables. * * Adds a bottom shadow to provide a visual cue indicating scrollability. * * See documentation: * {@link https://en.wikipedia.org/wiki/User:Jroberson108/Template:Sticky_table_start/ScrollCue} * * Configuration via mw.config: * * @config {boolean} stickyTableScrollCueIsForced * When true, forces the shadow on all skins/browsers for testing. * mw.config.set('stickyTableScrollCueIsForced', true); */(functionstickyTableScrollCue(){'use strict';// Load CSS from the associated stylesheet.mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Jroberson108/Template:Sticky_table_start/ScrollCue.css&action=raw&ctype=text/css','text/css');// Detect Minerva skin (covers all phones, including Android and iPhone).constisMinerva=mw.config.get('skin')==='minerva';// Detect WebKit: iOS, iPadOS, macOS, and rare WebKit browsers.constua=navigator.userAgent;constisWebKit=(/AppleWebKit/.test(ua)&&!/Chrome|Chromium|Edg|OPR|Firefox/.test(ua))||/\b(iPad|iPhone|iPod)\b/.test(ua);// Detect forced.constisForced=mw.config.get('stickyTableScrollCueIsForced');// Add custom class for WebKit browsers to enable CSS targeting.if(isWebKit||isForced){document.body.classList.add('webkit-sticky-table-scroll-cue');}/** * Set of per-element stable update functions for global resize event handling. * Each function updates the shadow position and visibility for its scroll * container element. These functions are stored as stable references on their * respective elements. * @type {Set<function>} */constscrollTables=newSet();/** * Debounce utility to ensure the handler runs only after a period of * inactivity, scheduled inside requestAnimationFrame for smoothness. * @param {function} fn Handler to debounce * @param {number} delay Debounce period (ms) * @returns {function} Debounced handler */functiondebounce(fn,delay){lettimer=null;returnfunction(){clearTimeout(timer);timer=setTimeout(()=>{requestAnimationFrame(fn);},delay);};}/** * Initialize sticky scroll cue for a scrollable element if not already wrapped. * Adds a wrapper and a shadow overlay that hides when scrolled to the bottom. * @param {HTMLElement} scrollDiv The scroll container element to enhance. */functionsetupStickyTable(scrollDiv){// Avoid double initialization on already processed containers.if(scrollDiv.parentElement.classList.contains('sticky-table-scroll-wrapper')){return;}// Create wrapper for scroll container and insert into DOM.constwrapper=document.createElement('div');wrapper.className='sticky-table-scroll-wrapper';scrollDiv.parentNode.insertBefore(wrapper,scrollDiv);wrapper.appendChild(scrollDiv);// Create and append the shadow element for the scroll cue overlay.constshadow=document.createElement('div');shadow.className='sticky-table-scroll-bottom';shadow.setAttribute('aria-hidden','true');// Decorative only.wrapper.appendChild(shadow);// Cache last known geometry values to avoid redundant DOM writes.letlastWidth=null;letlastLeft=null;/** * Update shadow position (width/left) if the scroll container moves or is * resized. */functionpositionShadow(){constnewWidth=scrollDiv.offsetWidth;constnewLeft=scrollDiv.offsetLeft;if(newWidth!==lastWidth){shadow.style.width=newWidth+'px';lastWidth=newWidth;}if(newLeft!==lastLeft){shadow.style.left=newLeft+'px';lastLeft=newLeft;}}/** * Show/hide shadow based on current scroll state. * Shadow hides when scrolled to the bottom (or nearly so). */functiontoggleShadow(){if(scrollDiv.scrollHeight-scrollDiv.scrollTop>scrollDiv.clientHeight+1){shadow.classList.remove('hide');}else{shadow.classList.add('hide');}}/** * Update function called on resize: positions and shows/hides shadow. */functionupdateScrollCue(){positionShadow();toggleShadow();}// React to scroll events (passive improves performance).scrollDiv.addEventListener('scroll',toggleShadow,{passive:true});// Initial setup.positionShadow();toggleShadow();// Store stable per-element update function for global updates.scrollDiv._stickyTableUpdate=updateScrollCue;scrollTables.add(updateScrollCue);}/** * Run global update on all registered sticky tables after resize. * Debounced, scheduled inside requestAnimationFrame for smoothness. */window.addEventListener('resize',debounce(function(){scrollTables.forEach(fn=>fn());},100));/** * Scan for scrollable tables only within the main article content area and * initialize them. Called after page load and can be rerun if new tables * appear dynamically. */functionstickyTableCueScan(){constcontent=document.getElementById('mw-content-text');if(content){content.querySelectorAll('.sticky-table-scroll').forEach(setupStickyTable);}}// On page load, scan/init tables if this is the Minerva skin or WebKit browser.if(isMinerva||isWebKit||isForced){stickyTableCueScan();}})();