select2: Select2 field not rendering as expected in Boostrap Tabs (display:none at loading)

Issue

When using a Select2 field inside a Bootstrap tab, if the field is inside one of the hidden tab when loading, the resulting field are not shown as expected.

I’m at work, unable to reproduce the issue on other system or browser, but I’ve got a JSFiddle that reproduce the behavior.

I suspect that the display:none; in .tab-content>.tab-pane for Bootstrap is the culprit. I don’t have time right now to test this without bootstrap with a simple switch for display:none/display:block.

Steps to reproduce the issue

Create 2 Boostrap Tabs, including one active. Create 1 new Select2 field per tab.

Expected behavior and actual behavior

When doing this, on the active tab when loading the DOM, the field will take 100% width and show on a second line under the label, which is the expected behavior.

On the hidden tab when loading the DOM, the field will take 100px (this is consistent will all the test I made) and be shown beside the label.

Moreover, the placeholder text for Multiple field is rendered also with 100px. So, even using a “hack” like $.fn.select2.defaults.set("width", null);, the placeholder for multiple field is still shown at 100px width (this is shown in the JSFiddle in tab 4)

Environment

Browsers

(Wasn’t able to test elsewhere yet)

  • Google Chrome
  • Mozilla Firefox
  • Internet Explorer

Operating System

(Wasn’t able to test elsewhere yet)

  • Windows
  • Mac OS X
  • Linux
  • Mobile

Libraries

  • jQuery version: 1.12.0
  • Bootstrap version: 3.3.6
  • Select2 version: 4.0.2-rc.1

Isolating the problem

  • This bug happens when using Select2 with the least plugins.
  • I can reproduce this bug in a jsfiddle

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 11
  • Comments: 16 (1 by maintainers)

Most upvoted comments

Any updates on this issue? Select2 is broken when form is in the hidden tab on page load. The same with bootstrap collapse.

For now I just fixed it with: .select2-container{ width: 100% !important; }

I know this issue was closed but I found a CSS-only method to fix the rendering on tabs, including placeholders.

.select2-container,
.select2-container li:only-child,
.select2-container input:placeholder-shown {
  width: 100% !important;
}
  • .select2-container addresses the width of the inputs on hidden tabs
  • .select2-container li:only-child applies when there are no selected items and you would expect the placeholder to be shown. there is always exactly one li when no items are selectedwith the placeholder when nothing is selected. if you tried putting the width on the entire li then selects with multiple inputs would have each selection take up an entire line
  • .select2-container input:placeholder-shown applies for inputs that have a placeholder that is visible, and is required because otherwise the width of the input would be 0px (the input is a child of the li)

The issue with the JavaScript solutions that call .trigger('change') is that they will also trigger any events listening on the inputs even though the values themselves are not changing. Also with looping, when you select each tab the focus will automatically be set to the last select2 instance on that tab which is likely undesirable.

Here’s a workaround I used for Bootstrap (v4) tabs. When calling select2, make sure to exclude selects that are not visible:

$('select:not(:hidden)').select2();

Next only render the hidden select elements when a bootstrap tab is changed:

$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
	$('select:not(.select2-hidden-accessible,:hidden)').select2();
});

TL;DR: A workaround:

See this jsfiddle for solution of both the incorrect size problem and a problem with only partially visible placeholders. Be careful with the width: 100% !important; solutions - they can cause sideefects.

The long answer:

As mentioned by @ivansammartino , this problem does not affect only Bootstrap tabs. And it also affects displayed placeholders, not just the selection box.

About the placeholder issue I have run into issues with the placeholder only being partially visible on elements which are loaded. I have been using {width: 100%} to init the Select2 across my system for a few years (a workaround for this issue, I did not know about this thread), but the placeholder did not show up properly. It is an issue tied with this original one, and you can see it on the “Tab 4” in the original jsfiddle , i.e., you can only see “Please select M” on the Tab 4’s [sic] “Tab3 Select2 Multiple” .

Why not use width: 100% !important; on the search field containing the placeholder? Because, for example, it causes the select2 search box to span multiple lines. No time to set-up a js fiddle, but if you set .select2-selection__rendered { width: 100% !important} and .select2-selection { width: 100%}, then you will see the multiline problem. That is the reason why the width is set like this in the first place - the inner .select2-search__field expands it’s width when text is written inside of it, to match the width of the written search query, but if no selections are present, the placeholder spans the entirety of the select box. That is quite smart, but only works if the select is actually visible when the width is calculated for the first time.

I have tracked the problem down to the resizeSearch() method of the select2 plugin. The problem is, as mentioned above, that it only resizes when text is written, not when the element becomes visible. I tried to trigger it directly, but had no luck doing that.

But if you select any option and then de-select it, the placeholder displays correctly, because the resizeSearch() is called in the background. So, I have decided to trigger the resize and it turns out that simply calling this works:

var currentData = $select.select2('data');
// We re-set the values, which will trigger the resizeSearch method.
$select.val(currentData).trigger('change');

Then the only issue was how to get the Select2 instances. Turns out this works:

$element.find('select').each(function (i, select) {
    var $select = $(select);
    // Make sure that the select has Select2. From the official Select2 docs:
    // https://select2.org/programmatic-control/methods#checking-if-the-plugin-is-initialized
    if($select.hasClass("select2-hidden-accessible")) {
          // Do your code here via $select.select2(...);
    }
}

Back to this issue, the 4220 So, I have noticed that you could use this mechanism to fix this original issue, as well as the placeholder issue, in one go.

There are multiple ways how you can solve it, I have found the ViaCss to be the best one. So, I have commented out the other ways, but they are mentioned in the example.

All the solutions can be found in the aforementioned jsfiddle.

I hope this helps somebody. I think this is the solution that @ivansammartino might be looking for.

Thanks to the users above me

I have used the original @sharky98 's jsfiddle as a starting point for my solution’s explanation. I was partially inspired by @nickelfault and others above.

IMHO this is not an easy bug to solve since a children whose parent is set to hidden is also hidden and therefore is removed from the normal flow. See this codepen.

You could apply this hack and control the width through the parent if you don’t want to wait until the bug be fixed.

I hope it helps.

The problem does not affect only Bootstrap tabs, but also much simpler setups where <select> is initially hidden: http://jsfiddle.net/aewx0u34/13/

Force width: 100% !important where the initial width is 0px (on .select2-container--default .select2-search--inline .select2-search__field selector) is only a partial solution for me, because fields starts growing in a bad way

Still no workaround for this, right?