User:JDrewniak (WMF)/notes/Close thyself - Managing MF Overlays


Who show/hides what? When? There are overlays, and there is OverlayManager, and there are also Drawers. Sometimes Overlays show/hide themselves, sometimes OverlayManager hides them. Drawers manage themselves.


Overlays manage their own DOM structure, (i.e. everything inside the overlay, Overlay.$el). They also manage their own DOM events and a few window events as well.

DOM managed by Overlay[edit]

this refers to the Overlay.

  • this.$el
    • .addClass( ‘overlay-ios’ );
    • .$( ‘.overlay-header h2 span’ ).addClass( ‘truncated-text’ );
    • .addClass( ‘visible’ );
    • .detach();
  • this.$spinner
    • .addClass( ‘hidden’ );
    • .removeClass( ‘hidden’ );
  • this.$overlayContent
    • .height
  • window
    • .scrollTo
  • $html
    • .addClass( overlay-enabled’ );
    • .removeClass( ‘overlay-enabled’ );
    • .find( ‘#mw-mf-page-center’ )

The bits that touch the actual document DOM are .detach() and window.scrollTo. These are executed in the Overlay show/hide methods.


Manages overlays based on routing and keeps track of open overlays.

DOM managed by OverlayManager[edit]

Before this patch, OverlayManager did not manage any DOM directly, instead, it called based on routing events.

Now OverlayManager accepts an appendToSelector and attaches the Overlay to the DOM in the _show method, but does not detach the overlay in the _hideOverlay method.

This means that currently Overlays cannot attach themselves to the DOM. This breaks loadingOverlays in rlModuleLoader, which calls

In both before/after that patch, the following code produces a “ghost overlay” . In console:

var om = mw.mobileFrontend.require( 'mobile.startup' ).OverlayManager.getSingleton();
var O = mw.mobileFrontend.require( 'mobile.startup/Overlay' )
var o = new O({ heading: 'why' })
var o2 = new O({ heading: 'hello' })
// On master o ); o2 ); om.hide( o2 );
// In production (still)
om._showOverlay( o ); om._showOverlay( o2 ); om._hideOverlay( o2 );
// Expected: o shown in all its glory. actual: body hidden

This is because when one overlay calls hide, it removes the overlay-enabled class from the HTML element, regardless of other open overlays.

Given this problem

  • Should OverlayManager append the overlay-enabled class to the HTML element, instead of having Overlay do it?
  • Should OverlayManager detach the Overlay from the DOM as well?
  • Should Drawers have their own manager? Which does the same thing as OverlayManager just without routing?
  • Can OverlayManager be refactored for both cases?

Open questions:

“When does an overlay not use a route?“

-> in rlModuleLoader, is called manually. Anything that calls loader.loadModule and returns a loadingOverlay, calls Overlay.hide() on that overlay before showing the new overlay.

  • Categories overlay
  • VE overlay
  • Language overlay
  • Nearby

All these modules call Overlay.hide(). For this reason, moving the DOM ‘detach’ behaviour to the OverlayManager would require refactoring these modules to use the overlayManager. In these instances however, the overlays are all shown through the OverlayManager.