Auto resizing the SELECT element according to selected OPTION's width
Solution 1
You are right there is no easy or built-in way to get the size of a particular select option. Here is a JsFiddle that does what you want.
It is okay with the latest versions of Chrome, Firefox, IE, Opera and Safari
.
I have added a hidden select #width_tmp_select
to compute the width of the visible select #resizing_select
that we want to be auto resizing. We set the hidden select to have a single option, whose text is that of the currently-selected option of the visible select. Then we use the width of the hidden select as the width of the visible select. Note that using a hidden span instead of a hidden select works pretty well, but the width will be a little off as pointed out by sami-al-subhi in the comment below.
$(document).ready(function() {
$('#resizing_select').change(function(){
$("#width_tmp_option").html($('#resizing_select option:selected').text());
$(this).width($("#width_tmp_select").width());
});
});
#resizing_select {
width: 50px;
}
#width_tmp_select{
display : none;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<select id="resizing_select">
<option>All</option>
<option>Longer</option>
<option>A very very long string...</option>
</select>
<select id="width_tmp_select">
<option id="width_tmp_option"></option>
</select>
Solution 2
Here's a plugin I just wrote for this question that dynamically creates and destroys a mock span
so it doesn't clutter up your html. This helps separate concerns, lets you delegate that functionality, and allows for easy reuse across multiple elements.
Include this JS anywhere:
(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:<select class="btn btn-select resizeselect"> <option>All</option> <option>Longer</option> <option>A very very long string...</option> </select>
-
JavaScript - Call
.resizeselect()
on any jQuery object:$("select").resizeselect()
Demo in jsFiddle and StackSnippets:
(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);
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<select class="resizeselect">
<option>All</option>
<option>Longer</option>
<option>A very very long string...</option>
</select>
Updated to include sizing suggestions from Garywoo & Eric Warnke
Solution 3
This working solution makes use here of a temporary auxiliary select
into which the selected option from the main select is cloned, such that one can assess the true width which the main select
should have.
The nice thing here is that you just add this code and it's applicable to every selects, thus no need to ids
and extra naming.
$('select').change(function(){
var text = $(this).find('option:selected').text()
var $aux = $('<select/>').append($('<option/>').text(text))
$(this).after($aux)
$(this).width($aux.width())
$aux.remove()
}).change()
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select>
<option>REALLY LONG TEXT, REALLY LONG TEXT, REALLY LONG TEXT</option>
<option>ABCDEFGHIJKL</option>
<option>ABC</option>
</select>
Solution 4
Try the following simple JavaScript and convert it in jQuery :)
<html><head><title>Auto size select</title>
<script>
var resized = false;
function resizeSelect(selected) {
if (resized) return;
var sel = document.getElementById('select');
for (var i=0; i<sel.options.length; i++) {
sel.options[i].title=sel.options[i].innerHTML;
if (i!=sel.options.selectedIndex) sel.options[i].innerHTML='';
}
resized = true;
sel.blur();
}
function restoreSelect() {
if (!resized) return;
var sel = document.getElementById('select');
for (var i=0; i<sel.options.length; i++) {
sel.options[i].innerHTML=sel.options[i].title;
}
resized = false;
}
</script>
</head>
<body onload="resizeSelect(this.selectedItem)">
<select id="select"
onchange="resizeSelect(this.selectedItem)"
onblur="resizeSelect(this.selectedItem)"
onfocus="restoreSelect()">
<option>text text</option>
<option>text text text text text</option>
<option>text text text </option>
<option>text text text text </option>
<option>text</option>
<option>text text text text text text text text text</option>
<option>text text</option>
</select>
</body></html>
Here is a jsfiddle for it: https://jsfiddle.net/sm4dnwuf/1/
Basically what it does is temporarily remove unselected elements when it is not in focus (causing it to size to just the size of the selected).
Solution 5
Here's a more modern vanilla JS approach to solve this. It's more or less the same principle like in this answer just without jQuery.
- Get the select element and listen for changes on it.
- Create a new select element and option and pass the text of the current
selectedIndex
to the option. - Add
position: fixed
andvisibility: hidden
styles to the new select element. This ensures, that it is not affecting your layout but its bounding box can still be measured. - Append the option to the select.
- Append the select to the original select element.
- Get the needed dimensions of that new one using
getBoundingClientRect().width
- Set the width of the original one based on the dimensions of the new one.
- Remove the new one.
- Dispatch a change event to trigger this logic initially.
const select = document.querySelector('select')
select.addEventListener('change', (event) => {
let tempSelect = document.createElement('select'),
tempOption = document.createElement('option');
tempOption.textContent = event.target.options[event.target.selectedIndex].text;
tempSelect.style.cssText += `
visibility: hidden;
position: fixed;
`;
tempSelect.appendChild(tempOption);
event.target.after(tempSelect);
const tempSelectWidth = tempSelect.getBoundingClientRect().width;
event.target.style.width = `${tempSelectWidth}px`;
tempSelect.remove();
});
select.dispatchEvent(new Event('change'));
<select>
<option>Short option</option>
<option>Some longer option</option>
<option>A very long option with a lot of text</option>
</select>
Th3Alchemist
Updated on October 13, 2021Comments
-
Th3Alchemist over 2 years
I've got this
select
element with differentoption
in it. Normally theselect
element would get its width from the biggestoption
element, but I want theselect
element to have the defaultoption
value's width which is shorter. When the user selects anotheroption
, theselect
element should resize itself so the whole text is always visible in the element.$(document).ready(function() { $('select').change(function(){ $(this).width($('select option:selected').width()); }); });
Problem:
- On Chrome (Canary) it always gets a width returned of 0.
- On Firefox the width get's added and not resized when selected a shorter option.
- Safari: Same result as Chrome.
-
Rory McCrossan over 10 yearsYour fiddle link is the same as the OPs.
-
Sami Al-Subhi almost 9 yearsgood approach but you have to make sure that
<option>
text has the same font/size as of the<span>
. If you remove#width_tmp{display : none;}
, you will find that<span>
is larger. This will result in larger<select>
than it should be. The longer the text the larger empty space in the<select>
. -
Garywoo over 7 yearsI would suggest changing the line
var $test = $("<span>").html(text);
tovar $test = $("<span style=\"font-size:"+$this.css("font-size")+"\">").html(text);
to ensure that the font size of the temporary span that is created matches that of the select element. If the font size of the span is smaller or larger, the dynamic resizing will be offset by the difference in size. -
rogerdpack almost 7 yearsHere's a pure javascript attempted version of this: jsfiddle.net/FSsG8/635 You you also might get some traction by attempting to match the font between the two selects, somewhat similar: stackoverflow.com/questions/6385533/…
-
Eric Warnke over 6 yearsWorks mostly! My default CSS using Bootstrap would always set the width of a select element to 100%, which returned incorrect widths. I changed the append from
body
to#myForm
and$("<span>")
to$('<span style="visibility:hidden">')
just for my peace of mind. -
lopezi about 6 yearsunfortunately it does not work on Ipad/Android tablets, any ideas why? I thought it could be that the font-size of the option is different in those devices but if I set a static font-size it does not solve it
-
lopezi about 6 yearsi fixed the above by giving a static font-size to the form element, what i was suggesting above was to give a static font-size on the script iself
-
João Pimentel Ferreira about 5 yearscheck my solution, it's much more simple and neat :) stackoverflow.com/a/55343177/1243247
-
João Pimentel Ferreira about 5 yearscheck my solution, it's much more simple and neat :) stackoverflow.com/a/55343177/1243247
-
Shane Hudson about 4 yearsThis has been downvoted but it does seem to work in Firefox (not Chrome).
-
Tammy Shipps almost 4 yearsIt's hard to read your code example with its formatting - it might be a good idea to clean it up a bit :)
-
kilian almost 3 yearsHere's a more modern vanilla JS approach to solve this: stackoverflow.com/a/67239947/4168960
-
kilian almost 3 yearsHere's a more modern vanilla JS approach to solve this: stackoverflow.com/a/67239947/4168960