8

I cannot calculate the offset position of an option within a select:

<select multiple="true">
    <option>asdf</option>
    <option id="bar">qwer</option>
</select>

$("#bar").offset()

Chrome returns (any jQuery versions like 1.7.2, 1.9 tested):

{top: 0, left: 0} 

FF returns:

{top: 26, left: 9}

I need the positions like FF returns. Is there a workaround for Chrome?

http://jsfiddle.net/YrRLx/

Chris
  • 8,031
  • 10
  • 41
  • 67
  • [This](http://stackoverflow.com/a/4240492/1427942) could be helpful. I don't know, but maybe there is no offset for `option` elements in chrome. – Eich Mar 12 '13 at 10:45
  • Could you tell a little bit more about what's the idea some of us might come up with another approach not involving `.offset()` – kidwon Mar 12 '13 at 21:12
  • is the offset of the select not good enough? – Huangism Mar 12 '13 at 21:18

2 Answers2

2

Ah... one solution I can think of is replacing the select with ul object, and attach event handlers to sync select to ul. Calling .offset() on li elements works as expected on both FF and Chrome.

So if you click the li elements, it will update its background color and update the selected value of the hidden select. This way, when form is submitted, correct value is sent.

http://jsfiddle.net/hqYqh/

HTML:

<select id="mySelect" multiple="true">
    <option>2011</option>
    <option>2012</option>
    <option>2013</option>
    <option>2014</option>
    <option>2015</option>
    <option>2016</option>
    <option>2017</option>
    <option>2018</option>
    <option>2019</option>
</select>

CSS:

ul.select {
    display: inline-block;
    border: 1px solid black;
    list-style: none;
    margin: 0;
    padding: 2px;
    height: 80px;
    overflow: auto;
    width: 50px;
}

ul.select li {
    background-color: none;
    cursor: pointer;
}

ul.select li.selected {
    background-color: skyblue;
}

JS:

var $select = $('#mySelect');
var $ul = $('<ul class="select">').on('click', 'li', function() {
    $(this).parent().find('li').removeClass('selected');
    $(this).addClass('selected');
    $select.val($(this).text()).change();
});

$select.find('option').each(function() {
    var $li = $('<li>').text($(this).text());
    $ul.append($li);
});

$select.hide().after($ul);

$select.change(function() {
    alert('Offset of ' + $(this).val() + ' is ' + JSON.stringify($('li.selected').offset()));
});

EDITED

So, I tried another method following Huangism's suggestion using offset() and scrollTop() of the select element.

Only thing that's not available is the height of the option element. So I tried to estimate using font-size, but it wasn't perfect, and I had to add magic number 3 on Chrome to get the exact offsets. If you can figure out the height of the option element, you don't have to deal with all the ul/li business above.

Here it is:

http://jsfiddle.net/hqYqh/2/

optionOffset = function($option) {
    var i = $option.index();
    var h = Number($option.css('font-size').replace(/px/, '')) + 3; // Cannot get the height of the option element :(
    var $select = $option.parent();
    var offset = $select.offset();
    offset.top += i*h - $select.scrollTop();
    return offset;
}
haejeong87
  • 847
  • 1
  • 10
  • 20
  • Nice solution in my opinion, but I suppose it depends on compatibility requirements (Mobile devices) if this is actually a valid solution – JakeJ Mar 15 '13 at 12:48
  • Ah, very good point. It should work on Mobile devices, but it won't trigger the native UI of the device. You can execute this code depending on the content of USER_AGENT header string. Perhaps I can help you further if you tell me why you need the offset of the options in the first place. – haejeong87 Mar 15 '13 at 13:02
  • I added a second possible solution after taking Huangism's advice. It might be useful for you. – haejeong87 Mar 15 '13 at 13:53
  • 1
    @crudolph You have accepted this as the answer, perhaps you could explain which part of the answer worked for you? And maybe elaborate on your original question so we can get an idea of why and possibly give an improved answer? – JakeJ Mar 15 '13 at 14:20
0

I would say that chrome returns 0 due to the option is not visible so it cannot calculate the top. This might occur in safari as well since it's all webkit browsers. The only thing I can think of is you get the offset top of the select and then do some calculations to get a approximate top of the option. For example, if an option is about 10px in height, you need to know how many options are before the one you want and work out how many pixels you need to add to the select top.

Huangism
  • 16,278
  • 7
  • 48
  • 74