Short description
Clicking a language tab scrolls the page edit interface. The field whose tab you clicked gets scrolled to the top of the viewport, and #langTab_… is left in the address bar.
It's most obvious inside a Repeater Matrix item partway down a long page, where it yanks the repeater item header, field label and tab row off the top of the screen. Present since the introduction of AdminThemeUikit.
Expected behavior
Clicking a language tab switches the visible language input and leaves the scroll position alone.
Actual behavior
The page scrolls so the newly shown language input sits at the top of the viewport.
Steps to reproduce
- Multi-language site using AdminThemeUikit, with a multi-language text field inside a Repeater Matrix item.
- Edit a page and scroll down to the repeater item.
- Click a language tab on that field — the page jumps.
Details / cause
LanguageTabs::addTab() renders each tab as a real fragment anchor:
"<a data-lang='$language->id' class='$aClass' href='#langTab_$id'>$title</a>"
and LanguageSupport::wrapInputfieldOutput() gives the matching per-language wrapper div that exact id:
$out = "<div class='$class' id='langTab_$id' data-language='$language->id'>" . …
so the fragment always resolves. The click handler in LanguageTabs.js (~line 110) switches the visible tab but never calls preventDefault(), so after it runs the browser follows the fragment and scrolls the just-shown div to the top of the viewport.
This was masked under the old default admin theme: jQueryUI is true there, $this.tabs() runs, and jQuery UI's _eventHandler calls event.preventDefault() unconditionally. AdminThemeUikit/init.php sets 'jQueryUI' => false, so no widget intercepts the click — and UIkit's own tab component deliberately won't either, since its maybeDefaultPreventClick() only suppresses href="#" and href="", not real fragments.
It actually fires on every language tab click, but only moves the page where there is room to scroll. That's why it reads as Repeater Matrix specific rather than universal: on a short form with title/body near the top there's little or nothing to scroll.
Also affects file/image description language tabs, which use the same pattern (InputfieldFile.module builds langTab_{id}__{language}).
Fix
e.preventDefault() in that click handler — preventDefault() rather than return false, so propagation stays intact for UIkit's delegated handler on the <ul uk-tab>. Verified on a live install.
PR: processwire/processwire#338
Setup / Environment
- ProcessWire 3.0.270 (dev)
- AdminThemeUikit
- LanguageSupport / LanguageSupportFields, FieldtypeRepeaterMatrix
Short description
Clicking a language tab scrolls the page edit interface. The field whose tab you clicked gets scrolled to the top of the viewport, and
#langTab_…is left in the address bar.It's most obvious inside a Repeater Matrix item partway down a long page, where it yanks the repeater item header, field label and tab row off the top of the screen. Present since the introduction of AdminThemeUikit.
Expected behavior
Clicking a language tab switches the visible language input and leaves the scroll position alone.
Actual behavior
The page scrolls so the newly shown language input sits at the top of the viewport.
Steps to reproduce
Details / cause
LanguageTabs::addTab()renders each tab as a real fragment anchor:"<a data-lang='$language->id' class='$aClass' href='#langTab_$id'>$title</a>"and
LanguageSupport::wrapInputfieldOutput()gives the matching per-language wrapper div that exact id:so the fragment always resolves. The click handler in
LanguageTabs.js(~line 110) switches the visible tab but never callspreventDefault(), so after it runs the browser follows the fragment and scrolls the just-shown div to the top of the viewport.This was masked under the old default admin theme:
jQueryUIistruethere,$this.tabs()runs, and jQuery UI's_eventHandlercallsevent.preventDefault()unconditionally.AdminThemeUikit/init.phpsets'jQueryUI' => false, so no widget intercepts the click — and UIkit's own tab component deliberately won't either, since itsmaybeDefaultPreventClick()only suppresseshref="#"andhref="", not real fragments.It actually fires on every language tab click, but only moves the page where there is room to scroll. That's why it reads as Repeater Matrix specific rather than universal: on a short form with title/body near the top there's little or nothing to scroll.
Also affects file/image description language tabs, which use the same pattern (
InputfieldFile.modulebuildslangTab_{id}__{language}).Fix
e.preventDefault()in that click handler —preventDefault()rather thanreturn false, so propagation stays intact for UIkit's delegated handler on the<ul uk-tab>. Verified on a live install.PR: processwire/processwire#338
Setup / Environment