J82
J82

Reputation: 8457

Target last li in row?

I have a simple list with list items that don't have any special classes.

<ul>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  <li>item</li>
  etc..
</ul>

It looks like this on the front end:

enter image description here

I want to be able to target the last li in each row (the ones highlighted in green). However, I am not sure how since these li's don't have any classes and depending on the user's screen, it can be a different li that ends up last in a row. Is there a way to target the last li in a row?

Edit: I think there was some confusion. All of the li items are in ONE ul.

Upvotes: 13

Views: 3108

Answers (12)

hzak
hzak

Reputation: 757

var foo = document.getElementById('foo');
// yields: Third  (3)
console.log(foo.lastElementChild.textContent);
<ul id="foo">
  <li>First (1)</li>
  <li>Second (2)</li>
  <li>Third (3)</li>
</ul>

Upvotes: 0

Danield
Danield

Reputation: 125443

How about a PURE CSS solution:

We can use a series of media queries to achieve this. With the media queries in place we can target the last item in each row and change its color.

Now this may sound like a cumbersome task, but if you are using a preprocessor such as LESS - this isn't such a difficult or error-prone task.

All we need to do is set up a few variables in the LESS mixin according to our needs - and we get the exact layout that we're after. Take a look....

CODEPEN (Resize to see this in action)

Usage is simple - just call the LESS mixin like so:

.box-layout(boxItemList,li,100px,120px,2,7,20px);

Where the mixin takes 7 parameters:

1) The list selector

2) The item selector

3) item width

4) item-height

5) min cols

6) max-cols

7) margin

We can change these parameters to whatever we need and we'll get the layout we need

DEMO #2

Here's CSS (LESS) code:

.box-layout(@list-selector,@item-selector, @item-width, @item-height, @min-cols, @max-cols, @margin)
{
  @item-with-margin: (@item-width + @margin);
  @layout-width: (@min-cols * @item-with-margin - @margin);
  @next-layout-width: (@layout-width + @item-with-margin);
  @layout-max-width: (@max-cols * @item-with-margin - @margin);
  @layout-min-width: (@min-cols * @item-with-margin - @margin);

  @list: ~" `'\n'`.@{list-selector}";
  @item: ~" `'\n'`@{item-selector}";

  @{list} {
    display: block;
    margin: 0 auto;
    list-style: none;
    border: 5px solid aqua;
    overflow: auto;
    padding:0;
    color: white;
    min-width: @layout-min-width;
    max-width: @layout-max-width;
  }

  @{item} {
    height: @item-height;
    width: @item-width;
    margin: 0 @margin 32px 0;
    background: tomato;
    float:left;
  }

  @media (max-width:@layout-min-width) {
         @{list} {
            width: @item-width;
           min-width: @item-width;
        }
    @{item} {
        margin-right:0;
      background: green;
      }
 }

  .loopingClass (@layout-width, @next-layout-width, @min-cols);
}

.loopingClass (@layout-width, @next-layout-width, @iteration) when (@layout-width <= @layout-max-width) {
  @media (min-width:@layout-width) {
         @{list} {
            width: @layout-width;
        }
    @{item} {
        &:nth-child(n) {
          margin-right: @margin;
          background: tomato;
        }
        &:nth-child(@{iteration}n) {
          margin-right: 0;
          background: green;
        }
        &:last-child {
          background: green;
        }
      }
    }
  .loopingClass(@next-layout-width, @next-layout-width + @item-with-margin, @iteration + 1);



}

.box-layout(boxItemList,li,100px,120px,2,7,20px);

Upvotes: 3

Vaibs_Cool
Vaibs_Cool

Reputation: 6156

function calculateLIsInRow() {
    var lisInRow = 0;
    $('ul li').each(function() {
        $(this).removeClass("back");
        if($(this).prev().length > 0) {
            if($(this).position().top != $(this).prev().position().top) {
               $(this).prev().addClass("back");
               // return false;
            }
            lisInRow++;
        } else {
            lisInRow++;   
        }
        if($(this).next().length > 0) {
        }
        else {
            $(this).addClass("back");
        }
    });
}

calculateLIsInRow();

$(window).resize(calculateLIsInRow);

JSFIDDLE

I think i have found the answer .. Please check by resizing the window..

Upvotes: 11

charlietfl
charlietfl

Reputation: 171679

Here's a solution that looks at the left offset for each element and compares to parent offset and any margin applied to the li's.

By looking at the left offset, if it matches the parent(adjusted for margin) then the previous element is end of row, as is the very last child of the UL.

It adds a class to the last in row that will allow you to do whatever is needed using CSS. It is also bound to window resize as it's trigger.

Widths are irrelevant in this solution.

var resizeTimer;

function findLastInRow(){
     var $list = $('ul'),
        offsetLeft = $list.offset().left,
        leftMargin = 5;
   /* reset */
    resetLastInRow($list);
    /* find end of rows and add class */
    $list.children().each(function (i) {
        var $li = $(this);                   
        if ($li.offset().left === offsetLeft+leftMargin) {
            $li.prev().addClass('to-hide');
        }
    }).last().addClass('to-hide');    

}

function resetLastInRow($parent){
    $parent.children().removeClass('to-hide');    
}
/* initiate using resize event and trigger resize on page load*/
$(window).resize(function(){
    /* throttle resize event */
    if(resizeTimer){
       clearTimeout(resizeTimer); 
    }
    resizeTimer=setTimeout(findLastInRow,50);

}).resize();

DEMO

Upvotes: 1

Suresh Ponnukalai
Suresh Ponnukalai

Reputation: 13988

Here is my approach. I am calculating how many columns are occupying each rows then I am applying the background colors using for loop.

JS

function calculateLIsInRow() {
var lisInRow = 0;
$('ul li').each(function() {
    if($(this).prev().length > 0) {
        if($(this).position().top != $(this).prev().position().top) return false;
        lisInRow++;
    }
    else {
        lisInRow++;   
    }
});   

$("ul li").css("background","#ffffff");

for(i=lisInRow; i<=$('ul li').length; i=i+lisInRow)
{
   $("ul li:nth-child("+i+")").css("background","green");
}
$("ul li:last-child").css("background","green"); 
}

calculateLIsInRow();

$(window).resize(calculateLIsInRow);

FIDDLE DEMO

Upvotes: 1

murnax
murnax

Reputation: 1222

If my understanding for your question is correct that every li has the same width.

And this is a function

function getLastItem(){
    $('li').removeClass('hello');
    var containerWidth = $('ul').eq(0).width();
    var totalItem = $('li').length;
    var itemWidth = $('li').eq(0).outerWidth(true); // plus margin
    var itemPerRow = Math.floor(containerWidth/itemWidth);
    var totalRows = Math.ceil(totalItem/itemPerRow);
    $('li:nth-child(' + itemPerRow + 'n), li:last-child()').addClass('hello');   
}

});

Hope my demo can help you

http://jsfiddle.net/4n7drqcz/

Upvotes: 2

Liftoff
Liftoff

Reputation: 25372

Here you go. The only way that I know to do this is to use jQuery to calculate the sizes of the container ul and the sizes of the inner elements.

This solution assumes the following:

  1. All of the <li> elements are arranged as if they were in a table, where all of the "cell" <li>s take up the same amount of width (including margins).
  2. You have applied the .last-type class to any <ul>s that you want to do this.

jQuery:

$(function(){

    resize();

    $(window).resize(function(){
        resize(); 
    });
});

function resize()
{
    var ul = $("ul.last-type");
    ul.each(function(){
        var ulWidth = $(this).width();
        var liWidth = $($(this).find("li")[0]).outerWidth(true);
    
        var lisPerLine = parseInt(ulWidth / liWidth);
        $(this).find("li").removeClass("marked");
        $(this).find("li:nth-child("+lisPerLine+"n)").addClass("marked");
    });

}

CSS:

ul
{
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0;
}

ul li
{
    display: inline-block;
    width: 75px;
    height: 75px;

    background: blue;
    margin: 15px;
    font-size: 100%;
}

ul li.marked,
ul li:last-child
{
    background: green;
}

JSFiddle


Edit:

I found out why there were a couple of screen sizes where the elements would be out-of-sync. Add zero font-size to the parent <ul> and set the real font-size in the <li>s.

ul
{
    font-size: 0;
}

ul li
{
    font-size: 100%;
}

And finally, I added the ul li:last-child selector to the same ul li.marked rule so that the last element in the last line would always be marked as well, even if it doesn't reach the end of the line.

Upvotes: 2

kapantzak
kapantzak

Reputation: 11750

Check this function (I had the same problem once and made this function, you may edit it as you want): EDIT WORKING ON RESIZE

DEMO (resize)

var wrapDetector = function(element) {
var $elem = $(element);
var wrapper = $(element).parent();
var wrapperWidth = wrapper.outerWidth();
var expToggle = $('#main-menu-wrapper.main-menu-expanded #header-toggle-buttons');
wrapper.attr('data-width', wrapperWidth);
var elemTotalWidth = 0;
$elem.each(function() {
    elemTotalWidth += $(this).outerWidth();
});

var wrapperWidthNew = wrapper.outerWidth();
var curWidth = 0;        
$elem.removeClass('lastInRow last-row');
$elem.each(function() {
    var that = $(this);
    var elemWidth = that.outerWidth();
    curWidth += elemWidth;        
    if (curWidth > wrapperWidthNew) {
        that.prev().addClass('lastInRow');
        curWidth = elemWidth;
    }
    that.attr('data-curWidth', curWidth);        
});
var lastInRow = $(element + '.lastInRow');

if (lastInRow.length > 0) {
    lastInRow.last().nextAll().addClass('last-row');        
} else {
    $elem.addClass('last-row');
}
}

wrapDetector('li');  

ADD THIS

$(window).resize(function() {
  wrapDetector('li');
});

Upvotes: 4

volx757
volx757

Reputation: 163

You could do something with the pixel distance of each last li from the border. Offset gets the distance from the top and the left

var offset = $("#target").offset();
display("target is at " + offset.left + "," + offset.top + " of document");

I'm sure you can come up with some reasonable maximum distance the last li will ever be from the border, then do

var screenWidth = $(document).width();
var distanceFromRightOfWindow = screenWidth - document.getelementsbyclassname("li").offset().left
if (distanceFromRightOfWindow < reasonableMaximumDistance)
/*Change the color to green*/

Upvotes: 1

Rahul Munjal
Rahul Munjal

Reputation: 2693

try this:

function getLastelement(){
var lastLi=$("ul>li:last-child");
// do your stuff on this lastLi which contains the direct last li child of your ul tag.
}

Upvotes: -2

Haleel
Haleel

Reputation: 7

You can get last child like this

 $('ul li:last-child').css('color','red');

Upvotes: -2

Hirav Sampat
Hirav Sampat

Reputation: 196

If you know how many elements are there in each row then you can use this

$('li:nth-child(Xn)')

Where X is the number of elements in each row. For the screen shot attached you can use..

$('li:nth-child(10n)')

Upvotes: -2

Related Questions