Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.
       🚧 True to our name, we’re still a work in progress. 🚧
   
       You’re welcome to explore, but account registration is currently invite-only as we finalize the setup. 
       Join our forum or follow Mastodon for updates. 
       Full Wiki launch coming soon!
   

MediaWiki:Gadget-subpagenav.js: Difference between revisions

MediaWiki interface page
Created page with "→============================================================================= Gadget-subpagenav.js Adds carets and touch/click support to Template:SubpageNav dropdowns. =============================================================================: ( function () { $( function () { var $navLinks = $( '.subpage-nav-links' ); if ( !$navLinks.length ) return; // Find all list items that contain a nested <ul> var $dropdowns =..."
 
Edit to allow template to work without metadata, and a simple "{{SubpageNav}}"
Line 1: Line 1:
/* =============================================================================
/* =============================================================================
   Gadget-subpagenav.js
   Gadget-subpagenav.js
   Adds carets and touch/click support to Template:SubpageNav dropdowns.
   Handles touch dropdowns and auto-builds nested menus from Special:PrefixIndex
   ============================================================================= */
   ============================================================================= */
( function () {
( function () {
Line 8: Line 8:
         if ( !$navLinks.length ) return;
         if ( !$navLinks.length ) return;


         // Find all list items that contain a nested <ul>
         // --- PART 1: Auto-Builder for Special:PrefixIndex ---
        $navLinks.find( '.subpage-auto-list' ).each( function () {
            var $autoContainer = $( this );
            var $newRootUl = $( '<ul></ul>' );
           
            // Loop through every link generated by PrefixIndex
            $autoContainer.find( 'a' ).each( function () {
                var $a = $( this ).clone();
                var fullPath = $a.text(); // e.g., "Guides/Basic Setup"
                var parts = fullPath.split( '/' );
               
                var $currentLevel = $newRootUl;
               
                // Build the folder structure
                for ( var i = 0; i < parts.length; i++ ) {
                    var part = parts[i];
                    var isLast = ( i === parts.length - 1 );
                   
                    // Look for existing folder/dropdown at this level
                    var $existingLi = $currentLevel.children( 'li[data-nav-part="' + part + '"]' );
                   
                    if ( !$existingLi.length ) {
                        $existingLi = $( '<li></li>' ).attr( 'data-nav-part', part );
                       
                        if ( isLast ) {
                            // It's the final link item
                            $a.text( part ); // Rename link to just the final part
                            $existingLi.append( $a );
                        } else {
                            // It's a folder (dropdown trigger)
                            $existingLi.append( $( '<span></span>' ).text( part ) );
                            $existingLi.append( '<ul></ul>' );
                        }
                        $currentLevel.append( $existingLi );
                    }
                   
                    // Move deeper into the tree
                    $currentLevel = $existingLi.children( 'ul' );
                }
            } );
           
            // Replace the messy PrefixIndex HTML with our clean nested list
            $autoContainer.replaceWith( $newRootUl );
        } );
 
        // --- PART 2: Dropdown Toggles (Carets & Mobile) ---
         var $dropdowns = $navLinks.find( 'li' ).has( 'ul' );
         var $dropdowns = $navLinks.find( 'li' ).has( 'ul' );
         $dropdowns.addClass( 'has-dropdown' );
         $dropdowns.addClass( 'has-dropdown' );
        // Append a caret to the top-level link or span
         $dropdowns.children( 'a, span' ).append( '<span class="subpage-nav-caret">▼</span>' );
         $dropdowns.children( 'a, span' ).append( '<span class="subpage-nav-caret">▼</span>' );


        // Handle clicks for toggling menus
         $navLinks.on( 'click', '.has-dropdown > a, .has-dropdown > span', function ( e ) {
         $navLinks.on( 'click', '.has-dropdown > a, .has-dropdown > span', function ( e ) {
             var $parentLi = $( this ).parent();
             var $parentLi = $( this ).parent();
             var isOpen = $parentLi.hasClass( 'is-open' );
              
 
             // Close siblings
             // Close other open dropdowns at the same level
             $parentLi.siblings( '.has-dropdown' ).removeClass( 'is-open' );
             $parentLi.siblings( '.has-dropdown' ).removeClass( 'is-open' );
 
           
             // Toggle current
             // Toggle current
             $parentLi.toggleClass( 'is-open' );
             $parentLi.toggleClass( 'is-open' );


            // If the item is just text (a span), prevent default click behavior
            // If it's a link (a tag), we usually want to let them navigate to it,
            // but on mobile, the first tap should ideally open the menu.
            // For safety, if it's a span, we stop the event.
             if ( $( this ).is( 'span' ) ) {
             if ( $( this ).is( 'span' ) ) {
                 e.preventDefault();
                 e.preventDefault();
Line 35: Line 72:
         } );
         } );


        // Close dropdowns when clicking anywhere outside the nav
         $( document ).on( 'click', function ( e ) {
         $( document ).on( 'click', function ( e ) {
             if ( !$( e.target ).closest( '.subpage-nav-links' ).length ) {
             if ( !$( e.target ).closest( '.subpage-nav-links' ).length ) {

Revision as of 12:31, 22 March 2026

/* =============================================================================
   Gadget-subpagenav.js
   Handles touch dropdowns and auto-builds nested menus from Special:PrefixIndex
   ============================================================================= */
( function () {
    $( function () {
        var $navLinks = $( '.subpage-nav-links' );
        if ( !$navLinks.length ) return;

        // --- PART 1: Auto-Builder for Special:PrefixIndex ---
        $navLinks.find( '.subpage-auto-list' ).each( function () {
            var $autoContainer = $( this );
            var $newRootUl = $( '<ul></ul>' );
            
            // Loop through every link generated by PrefixIndex
            $autoContainer.find( 'a' ).each( function () {
                var $a = $( this ).clone();
                var fullPath = $a.text(); // e.g., "Guides/Basic Setup"
                var parts = fullPath.split( '/' );
                
                var $currentLevel = $newRootUl;
                
                // Build the folder structure
                for ( var i = 0; i < parts.length; i++ ) {
                    var part = parts[i];
                    var isLast = ( i === parts.length - 1 );
                    
                    // Look for existing folder/dropdown at this level
                    var $existingLi = $currentLevel.children( 'li[data-nav-part="' + part + '"]' );
                    
                    if ( !$existingLi.length ) {
                        $existingLi = $( '<li></li>' ).attr( 'data-nav-part', part );
                        
                        if ( isLast ) {
                            // It's the final link item
                            $a.text( part ); // Rename link to just the final part
                            $existingLi.append( $a );
                        } else {
                            // It's a folder (dropdown trigger)
                            $existingLi.append( $( '<span></span>' ).text( part ) );
                            $existingLi.append( '<ul></ul>' );
                        }
                        $currentLevel.append( $existingLi );
                    }
                    
                    // Move deeper into the tree
                    $currentLevel = $existingLi.children( 'ul' );
                }
            } );
            
            // Replace the messy PrefixIndex HTML with our clean nested list
            $autoContainer.replaceWith( $newRootUl );
        } );

        // --- PART 2: Dropdown Toggles (Carets & Mobile) ---
        var $dropdowns = $navLinks.find( 'li' ).has( 'ul' );
        $dropdowns.addClass( 'has-dropdown' );
        $dropdowns.children( 'a, span' ).append( '<span class="subpage-nav-caret">â–¼</span>' );

        $navLinks.on( 'click', '.has-dropdown > a, .has-dropdown > span', function ( e ) {
            var $parentLi = $( this ).parent();
            
            // Close siblings
            $parentLi.siblings( '.has-dropdown' ).removeClass( 'is-open' );
            
            // Toggle current
            $parentLi.toggleClass( 'is-open' );

            if ( $( this ).is( 'span' ) ) {
                e.preventDefault();
            }
        } );

        $( document ).on( 'click', function ( e ) {
            if ( !$( e.target ).closest( '.subpage-nav-links' ).length ) {
                $( '.subpage-nav-links .has-dropdown' ).removeClass( 'is-open' );
            }
        } );
    } );
}() );