0

I have this horizontal list of items

<section class="viewport">
    <ul class="item-list">
        <li class="item">A</li>
        <li class="item">B</li>
        ...
    </ul>
</section>

The list is movable in the horizontal direction (left/right). Checkout this jsfiddle Each item is of random width!

Now, assume that the item-list element is moved. The question is, which item is at the middle of the viewport ? A possible solution (not tested) is to calculate the offset of each element, subtract the movement, etc etc. But performance is very important, so I was wondering if someone knows a better solution ?

UPDATE: as a bonus question, how do you remove the space between the items. as is seen in the jsfiddle ?

Jeanluca Scaljeri
  • 26,343
  • 56
  • 205
  • 333
  • 1
    I assume you mean middle when the window is the same width as what it requires to fit all the items? – Kevin B Mar 11 '14 at 19:24
  • 2
    For the space issue you could refer [this answer](http://stackoverflow.com/questions/5078239/how-to-remove-the-space-between-inline-block-elements) – Hashem Qolami Mar 11 '14 at 19:24
  • yes **middle** of the `viewport` element (black vertical line!) and thanks Hashem!! +1 – Jeanluca Scaljeri Mar 11 '14 at 19:29
  • Right, but middle of the viewport changes based on browser width, meaning the middle item may not be the one that is the middle item relative to all of the items width (so on a small window item 1 will always be the middle. – Kevin B Mar 11 '14 at 19:34
  • Good point, however, the middle of the viewport can be precalculated (in my case) so thats not an issue here. We could set the black line at 400px – Jeanluca Scaljeri Mar 11 '14 at 19:38

2 Answers2

3

The answer to the bonus question: remove the spaces between the list item elements. When list items (or anything) are inlined, they are treated kind of like text, in that whitespace between them creates an actual space. The only way to remove them (short of using a different method, like floating) is to remove the space between the ending tag of one and the opening tag of another.

What I frequently do when I need inline lists to not have a space is to move the closing li right before the opening li. It doesn't look super great, but I think it's the clearest of the available options.

<ul>
    <li>A
    </li><li>B
    </li><li>C
    </li><li>D
    </li>
</ul>

As for figuring out the middle element, iterate through the list, adding the width of each item until the widthSum + offset of the UL is greater than half the width of the viewport.

I've created an updated fiddle here: http://jsfiddle.net/x9v3W/5/

Here is the basic code:

var width = 0,
    targetWidth = $('.viewport').width(),
    offset = $('.item-list').offset().left,
    $item = $('.item-list :first-child');

while ($item && width + offset < targetWidth / 2) {
    width += $item.width();
    $item = $item.next();
}

alert($item.prev().text());

Note that this code doesn't take in to account everything (like borders, paddings, margins, etc.) so you may need to adjust the code for the exact HTML you end up using. This should give you an idea of the technique itself though.

samanime
  • 25,408
  • 15
  • 90
  • 139
  • I like the idea that this one doesn't actually call .width and .offset on every item, instead only calling .width until it finds the middle. – Kevin B Mar 11 '14 at 19:55
1

Here's one way, sort the items based on the distance from the middle of the item to the middle offset (supplied by you in comments,) then get the first item.

http://jsfiddle.net/x9v3W/6/

middleOffset = 400; // precalculated, from comments

middleItem = $(".item").get().sort(function(a,b){
    var $a = $(a), $b = $(b);
    var aDiff = Math.abs(($a.offset().left +$a.width()/2) - middleOffset);
    var bDiff = Math.abs(($b.offset().left +$b.width()/2) - middleOffset);
    return aDiff == bDiff ? 0 : aDiff > bDiff ? 1 : -1;
});
$(middleItem).css("background-color","blue");

Also, fixed your space issue in fiddle, based on answers at the question supplied in comments.

Kevin B
  • 94,570
  • 16
  • 163
  • 180