This issue occurs because the #width_tmp
is nested under .input-group-btn
so it inherits the font-size: 0;
property from bootstrap. Therefore, the calculated width of the element is always zero:
To fix this, just move the <span id="width_tmp"></span>
so it's not inside the input group.
Pure JS Solution
// resize on initial load
document.querySelectorAll("select").forEach(resizeSelect)
// delegated listener on change
document.body.addEventListener("change", (e) => {
if (e.target.matches("select")) {
resizeSelect(e.target)
}
})
function resizeSelect(sel) {
let tempOption = document.createElement('option');
tempOption.textContent = sel.selectedOptions[0].textContent;
let tempSelect = document.createElement('select');
tempSelect.style.visibility = "hidden";
tempSelect.style.position = "fixed"
tempSelect.appendChild(tempOption);
sel.after(tempSelect);
sel.style.width = `${+tempSelect.clientWidth + 4}px`;
tempSelect.remove();
}
Demo in jsFiddle and Stack Snippets
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
// resize on initial load
document.querySelectorAll("select").forEach(resizeSelect)
// delegated listener on change
document.body.addEventListener("change", (e) => {
if (e.target.matches("select")) {
resizeSelect(e.target)
}
})
function resizeSelect(sel) {
let tempOption = document.createElement('option');
tempOption.textContent = sel.selectedOptions[0].textContent;
let tempSelect = document.createElement('select');
tempSelect.style.visibility = "hidden";
tempSelect.style.position = "fixed"
tempSelect.appendChild(tempOption);
sel.after(tempSelect);
sel.style.width = `${+tempSelect.clientWidth + 4}px`;
tempSelect.remove();
}
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select>
<option value="2">Some longer option</option>
<option value="1">Short option</option>
<option value="3">An very long option with a lot of text</option>
</select>
<select>
<option>Short option 2</option>
<option>Some longer option 2</option>
<option selected>An very long option with a lot of text 2</option>
</select>
<!-- end snippet -->
jQuery Plugin Solution
Another alternative would be to use a plugin to handle the whole operation start to finish. Here's one I just whipped up.
The plugin dynamically creates and destroys the span
so it doesn't clutter up your html. This helps separate concerns and lets you delegate that functionality to another file and easily re-use with multiple elements across multiple pages.
(function($, window){
$(function() {
let arrowWidth = 30;
$.fn.resizeselect = function(settings) {
return this.each(function() {
$(this).change(function(){
let $this = $(this);
// get font-weight, font-size, and font-family
let style = window.getComputedStyle(this)
let { fontWeight, fontSize, fontFamily } = style
// create test element
let text = $this.find("option:selected").text();
let $test = $("<span>").html(text).css({
"font-size": fontSize,
"font-weight": fontWeight,
"font-family": fontFamily,
"visibility": "hidden" // prevents FOUC
});
// add to body, get width, and get out
$test.appendTo($this.parent());
let width = $test.width();
$test.remove();
// set select width
$this.width(width + arrowWidth);
// run on start
}).change();
});
};
// run by default
$("select.resizeselect").resizeselect();
})
})(jQuery, window);
You can initialize the plugin in one of two ways:
-
HTML - Add the class .resizeselect
to any select element:
<!-- language: lang-html -->
<pre><code><select class="btn btn-select <b>resizeselect</b>">
<option>All</option>
<option>Longer</option>
<option>A very very long string...</option>
</select>
</code></pre>
-
JavaScript - Call .resizeselect()
on any jQuery object:
<!-- language: lang-js -->
<pre><code>$("select").<b>resizeselect</b>()</code></pre>
Demo in jsFiddle and StackSnippets:
<!-- begin snippet: js hide: true console: false babel: false -->
<!-- language: lang-js -->
(function($, window){
$(function() {
let arrowWidth = 30;
$.fn.resizeselect = function(settings) {
return this.each(function() {
$(this).change(function(){
let $this = $(this);
// get font-weight, font-size, and font-family
let style = window.getComputedStyle(this)
let { fontWeight, fontSize, fontFamily } = style
// create test element
let text = $this.find("option:selected").text();
let $test = $("<span>").html(text).css({
"font-size": fontSize,
"font-weight": fontWeight,
"font-family": fontFamily,
"visibility": "hidden" // prevents FOUC
});
// add to body, get width, and get out
$test.appendTo($this.parent());
let width = $test.width();
$test.remove();
// set select width
$this.width(width + arrowWidth);
// run on start
}).change();
});
};
// run by default
$("select.resizeselect").resizeselect();
})
})(jQuery, window);
<!-- language: lang-css -->
.btn-select {
color: #333;
background-color: #f5f5f5;
border-color: #ccc;
padding:7px;
}
<!-- language: lang-html -->
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/css/bootstrap.css" rel="stylesheet"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/js/bootstrap.js"></script>
<div class="form-group">
<div class="input-group">
<span class="input-group-btn">
<select class="btn btn-select resizeselect">
<option>All</option>
<option>Longer</option>
<option>A very very long string...</option>
</select>
</span>
<input type="text" class="form-control" placeholder="Search by...">
<span class="input-group-btn">
<button class="btn btn-default" type="button">
<i class="fa fa-search"></i>
</button>
</span>
</div>
</div>
<!-- end snippet -->