News:

You may pay someone to create your store, or you visit our seminar and become a professional yourself with the silver certification

Main Menu

[BUG] Product edit - Categories field collapses to width: 0px after tab switch

Started by sirius, Yesterday at 16:55:50 PM

Previous topic - Next topic

sirius

[BUG] Product edit - Categories field collapses to width: 0px after tab switch (Chosen.js + UIkit hidden tab race condition)

VirtueMart version: 4.6.8
Joomla version: 5.x
PHP version: 8.x
Template: vmadmin (default backend template)

Description

When editing a product, switching away from the "Product Information" tab and then switching back causes the Categories multi-select field to collapse to zero width and become unusable. The browser inspector shows:

<div class="chosen-container chosen-container-multi" id="categories_chosen" style="width: 0px;">
Steps to reproduce

  • Open any product for editing (the "Product Information" tab is active by default).
  • Click any other tab, e.g. "Product Images".
  • Click back on "Product Information".
  • The Categories field is now crushed to 0px width and invisible.

The bug also reproduces reliably on first load when the product was previously saved while on a tab other than "Product Information" (UIkit stores the active tab in a cookie - on reload, the "Information" tab starts hidden).

Root cause

Virtuemart.loadCategoryTree() (in
components/com_virtuemart/assets/js/ajax_catree.js) fires an AJAX request during
document.ready. On AJAX success, it calls:

jQuery('select#' + id).chosen({ select_some_options_text: Virtuemart.selectSomeCategory });
// no width option passed

Chosen.js computes its container width from the
<select> element at the moment of initialization. If the AJAX response arrives while the "Product Information"
<li> is hidden by UIkit's switcher (
display: none), the measured width is 0. Chosen then stamps
style="width: 0px;" on its container, and nothing corrects it when the tab becomes visible again.

This is a classic AJAX-vs-hidden-element race condition. The AJAX delay is unpredictable, and once the user has switched tabs, the hidden tab has zero dimensions.

A secondary trigger:
Virtuemart.updateChosenDropdownLayout() (generated by
vmjsapi.php) passes
width: '100%' to
.chosen(), which avoids the problem. But the categories field carries the class
vm-chzn-add, which intentionally excludes it from that initializer (
select:not(.vm-chzn-add)). So the categories select is always initialized through the AJAX path, without a width fallback.

Proposed fix - two-part

1. Root fix (in ajax_catree.js): pass
width: '100%' to the
.chosen() call so the container always gets a percentage-based width instead of a pixel-measured one.

// Before (line ~87):
jQuery('select#'+id).chosen({select_some_options_text: Virtuemart.selectSomeCategory});

// After:
jQuery('select#'+id).chosen({select_some_options_text: Virtuemart.selectSomeCategory, width: '100%'});

2. Safety net (UIkit show event): UIkit 3 dispatches
show with
bubbles: true on each
<li> of the switcher when it becomes active (confirmed in uikit.js -
createEvent(e, bubbles = true)). Adding a listener on the tabs container catches any other Chosen widget that may have suffered the same fate:

jQuery(document).ready(function($) {
var tabsContainer = document.getElementById('vmuikit-admin-ui-tabs');
if (!tabsContainer) { return; }
tabsContainer.addEventListener('show', function(e) {
$(e.target).find('.chosen-container').each(function() {
if (this.style.width === '0px') {
$(this).css('width', '100%');
}
});
});
});

This listener is harmless on every other tab switch (no
.chosen-container with
width: 0px means the
.each() body never runs).

Workaround for existing installations

Add the safety-net JS snippet above to a template override of
administrator/templates/vmadmin/html/com_virtuemart/product/product_edit_information.php via
vmJsApi::addJScript().

Thanks for reading all of that
J5.4.6 | PHP 8.4.10 + Redis + APC + Opcode
Litespeed | MariaDB 10.6.22
VM Prod : 4.6.8 11258 | VM Test : 4.9.3 11254