Reputation: 8457
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:
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
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
Reputation: 125443
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....
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
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
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);
I think i have found the answer .. Please check by resizing the window..
Upvotes: 11
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();
Upvotes: 1
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);
Upvotes: 1
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
Upvotes: 2
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:
<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)..last-type
class to any <ul>
s that you want to do this.$(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");
});
}
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;
}
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
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
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
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
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
Reputation: 7
You can get last child like this
$('ul li:last-child').css('color','red');
Upvotes: -2
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