MediaWiki:Gadget-subpagenav.js: Difference between revisions
MediaWiki interface page
More actions
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 | ||
  |   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; | ||
     // |      // --- 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' ); | ||
     $dropdowns.children( 'a, span' ).append( '<span class="subpage-nav-caret">▼</span>' ); |      $dropdowns.children( 'a, span' ).append( '<span class="subpage-nav-caret">▼</span>' ); | ||
     $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(); | ||
       |         | ||
 |        // Close siblings | ||
       // Close | |||
       $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 ( $( this ).is( 'span' ) ) { |        if ( $( this ).is( 'span' ) ) { | ||
         e.preventDefault(); |          e.preventDefault(); | ||
| Line 35: | Line 72: | ||
     } ); |      } ); | ||
     $( 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' );
}
} );
} );
}() );