VirtueMart Forum

VirtueMart 2 + 3 + 4 => Virtuemart Development and bug reports => Topic started by: sirius on June 25, 2026, 16:55:50 PM

Title: [BUG] Product edit - Categories field collapses to width: 0px after tab switch
Post by: sirius on June 25, 2026, 16:55:50 PM
[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


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