Page MenuHomePhabricator

Initial jump to fragment in page fails when collapsed element above
Closed, DuplicatePublic

Description

Problem

Imagine the following code:

{| class="wikitable mw-collapsible mw-collapsed"
! header
|-
| some lengthy contents higher than window height
|}
<div id="niceInfo">
some contents to be displayed initially, higher than window height
</div>

Now call that /wiki/page#niceInfo URL.

The browser will jump to the fragment initially, but then jQuery::makeCollapsible() takes action and the rendered area above shrinks. Finally, the page position is in the middle of nowhere.

For a live example see: https://de.wikipedia.org/wiki/WP:START#Informationen

  • That is the headline of the fourth (blue) block.
  • Above there are three coloured blocks.
  • Above there are six collapsible collapsed blocks.
  • Each Vorab-Information zeigen/ausblenden opens a brief summary of the linked page.

Watch carefully what happens on page loading.

  • Only the long list of languages in the left column delivers some content.
  • The vertical position the browser tries to keep lies below the carpet.

If some machines or browsers are very slow or very, very smart, they might reach the correct position.

Considerations

Main procedure .makeCollapsible() might be called several times for inner content (LivePreview, VE), but anchoring performed only on very first document loading. Therefore some alreadyInitialized memory is required.

That might be established on:

  • rMW resources/src/mediawiki/page/startup.js
  • rMW resources/src/mediawiki/page/ready.js
  • rMW resources/src/jquery/jquery.makeCollapsible.js

One way is to introduce mw.page.initialized = true; which is to be set at the end of the page/ready.js hook procedure, and is available to any other JS, too (and documented), and permits some overriding.

Otherwise local in page/ready.js module context.

The boolean value may be passed to makeCollapsible() options{} object or (better) as a second argument. and the entire collapsed business may be implemented there.

Since makeCollapsible() might be called by other and existing code, here an argument value of undefined or false shall mean "re-entering", but true means "very first call ever".

Solution fragments

Within makeCollapsible()

if ( ! initialized  &&  Location.hash.length > 1 ) {
   // Location.hash starts with # and has expected coding
   // redirect hash preservation covered??
   $fragment = $( Location.hash );    // or like     $content.find()
   if ( $fragment.length ) {
      // anchor is present on page (or in $content only)
      $collapsed = $( this ).find( '.mw-collapsed' );
      if ( $collapsed.length ) {
         // and initial collapsing is desired somewhere
         $fragment = $fragment.eq( 0 );
         memoY = $fragment.offset().top;
         // or    memoY = $fragment.scrollTop();
         if ( $collapsed.eq( 0 ).offset().top >= memoY ) {
            // first collapsed below fragment, or the same element
            memoY = false;
         }
      }
   }
}
/*
 * makeCollapsible business as usual.
*/
if ( memoY ) {
   // something collapsed above the $fragment
   // perhaps wait some 500 ms?
   // do something smart that works on all browsers to adjust
   // In redirect context there is/was some solution to re-jump?
}