Target last li in row?
Asked Answered
A

12

13

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.

Assuntaassur answered 22/12, 2014 at 7:3 Comment(13)
All these li's are inside one ul and you want to target every last li of each row?Statistical
Hows the html structure .. is it all one ul or one ul per line?Tunis
@Statistical Yes, that is correct.Assuntaassur
@Tunis All of the li items are in one ul.Assuntaassur
You could check the offsetTop of nextElementSibling, if it differs from the offsetTop of the target element, the target is the last li on a row.Cascio
You didn't have same number of li 's in an row all rows have differnt l's . it's not possible in jqueryBasie
@JqueryKing why not?Praseodymium
@Assuntaassur to be fair you really should provide the basic layout css, float vs inline etcPraseodymium
What charlietfl said. You're just getting mud thrown at your wall and seeing what sticks if you don't provide any of the CSS that sets up this layout. None of us have any idea why Vaibs_Cool's answer works for you - and honestly I don't think they quite understand either.Arlaarlan
@Assuntaassur - For what it's worth - I posted a pure css solution hereHandsel
Close enough to this: #27539628Alveolus
@SalmanA - actually there's a difference. Over there, the list items have dynamic length - which means that my media queries solution here won't work.Handsel
@Handsel details differ but question is still the same though; however this is tagged JavaScript so should not be duped.Alveolus
T
11
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..

Tunis answered 22/12, 2014 at 7:21 Comment(3)
Please provide an explanation of what is going on in your example.Erhart
I am checking through all li's if the prev of current li has content .. for eg for first li the length of prev will 0 so lisInRow value will be 1 and will go like this .. And if length is greater than zero if will go in the loop and check the position .. if the position is not matched then it will add class to the prev li.Tunis
Nice solution ...Thanks a lotTortoiseshell
S
4

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');
});
Statistical answered 22/12, 2014 at 7:12 Comment(9)
Which line exactly is the one that performs the requested task?Statistical
But only one li is last child of the parent ul!Statistical
I think that he wants to detect when each element gets wrapped and pushed to the next line.Statistical
Actually this works only, when you've two "columns", otherwise every even li has a red background.Cascio
@Cascio Works fine. Function can be improved but still - this is the only working solution on this page.Glovsky
It's not solution for this question. it's not works for all columnsBasie
@Glovsky Fine? Please resize the window at jsFiddle so that you can see three cols. That's not what OP wants.Cascio
So he can call the function every time he resizesStatistical
@Statistical Better, but still suffers some occasional "diagonal effect" depending on the window size.Cascio
H
3

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);
Handsel answered 22/12, 2014 at 11:10 Comment(0)
S
2

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.

Sergu answered 22/12, 2014 at 7:24 Comment(1)
Would the downvoter care to comment as to why he/she downvoted?Sergu
A
2

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/

Annettannetta answered 22/12, 2014 at 7:34 Comment(0)
T
1

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*/
Terribly answered 22/12, 2014 at 7:27 Comment(2)
good idea for full width rows but will need to also check if last row is fullPraseodymium
yeah true, use :last-child selector for that oneTerribly
S
1

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

Slit answered 22/12, 2014 at 7:56 Comment(0)
P
1

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

Praseodymium answered 22/12, 2014 at 8:3 Comment(0)
R
0

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>
Ronald answered 7/11, 2019 at 1:4 Comment(0)
E
-2

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)')
Etam answered 22/12, 2014 at 7:10 Comment(1)
I don't know how many elements will be in each row. It would depend on the user's screen.Assuntaassur
L
-2

You can get last child like this

 $('ul li:last-child').css('color','red');
Lareine answered 22/12, 2014 at 7:14 Comment(0)
B
-2

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.
}
Bracci answered 22/12, 2014 at 7:17 Comment(1)
while your answer may solve the op's problem, you should also explain what you did, why the op's approach (if any) didn't work. Link-only and code-only answers are very likely to get deleted.Interstice

© 2022 - 2024 — McMap. All rights reserved.