Dynamically assign z-index in ascending/descending order to unknown number of elements with a certain class

StackOverflow https://stackoverflow.com/questions/23370894

Question

I have an element with class .book which contain random number of elements with class .page.

<div class='book'>
    <div class='page'></div>
    <div class='page'></div>
    <div class='page'></div>
    <div class='page'></div>
    <div class='page'></div>
</div>

The .pagess are laid out on top of each other like pages in a book so the first .page element needs to have the highest z-index which is equal to the number of .page elements in .book.

Currently I manually assign each .page's z-index in the CSS, but I'd like more flexibility.
Is there a way to count the number of .page elements in .book and dynamically assign z-index appropriately using javascript? I'd also like the inverse to happen. When a .page is flipped I'd like the z-index order assigned to it in reverse order.

Example:
Normal:

<div class='book'>
    <div class='page' style='z-index: 5'></div>
    <div class='page' style='z-index: 4'></div>
    <div class='page' style='z-index: 3'></div>
    <div class='page' style='z-index: 2'></div>
    <div class='page' style='z-index: 1'></div>
</div>

First three .pages flipped:

<div class='book'>
    <div class='page flipped' style='z-index: 1'></div>
    <div class='page flipped' style='z-index: 2'></div>
    <div class='page flipped' style='z-index: 3'></div>
    <div class='page' style='z-index: 2'></div>
    <div class='page' style='z-index: 1'></div>
</div>

I'm okay with jQuery.

JSFIDDLE

UPDATE:
Everyone's answers works as intended on the reduced test case but when I used it on the original work, the 'z-index' order didn't seem to update.
Fiddle: http://jsfiddle.net/jklm313/HwR9c/ (sans your code).
I'm using jQuery to toggle .flipped class on the '.page' element when it's clicked:

$('.page').click(function() {
    $(this).toggleClass('flipped');
});

Here's where I'm applying this to, please feel free to use them:
Responsive Book Layout
Book

Was it helpful?

Solution 2

This also works:

$(".book").each(function(){
    var pages=$(this).find(".page")
    var pages_flipped=$(this).find(".flipped")
    pages.each(function(){
        $(this).css("z-index",pages.length-pages.index($(this)))
    })
    pages_flipped.each(function(){
        $(this).css("z-index",pages_flipped.index($(this))+1)
    })    
})

As for your update, correct code is:

$('.page').click(function() {
    $(this).removeClass('no-anim').toggleClass('flipped');
    e.stopPropagation();
});

If not you are adding a handler that executes before page click and it stops click event propagation

OTHER TIPS

To assign to the non-flipped elements, in order, this should do the trick:

var max = $('div.book div.page:not(.flipped)').length;
$('div.book div.page:not(.flipped)').each(
    function() {
        $(this).css('z-index', max--);
    }
);

Then, to assign to the flipped elements:

var count = 0;
$('div.book div.flipped').each(
    function() {
        $(this).css('z-index', ++count);
    }
);

Since you have multiple book elements on a page, then you would construct it somewhat differently:

$('div.book').each(
    function() {
        var max = $(this).children('div.page:not(.flipped)').size();
        $(this).children('div.page:not(.flipped)').each(
            function() {
                $(this).css('z-index', max--);
            }
        );

        var count = 0;
        $(this).children('div.flipped').each(
            function() {
                 $(this).css('z-index', ++count);
            }
        );
    }
);

To cause this to get reassigned when you click on the book, then put the above code into a function, and call that function in your book click event, like so:

// Slightly modified to work properly, and calls the index function
$('.page').click(function() {
    $(this).removeClass('no-anim').toggleClass('flipped');
    e.stopPropagation();
    indexPages();
});

// Putting zindex assignment into a function for reuse
function indexPages() {
  $('div.book').each(
        function() {
            var max = $(this).children('div.page:not(.flipped)').size();
            $(this).children('div.page:not(.flipped)').each(
                function() {
                    $(this).css('z-index', max--);
                }
            );

            var count = 0;
            $(this).children('div.flipped').each(
                function() {
                     $(this).css('z-index', ++count);
                }
            );
        }
    );
}

here you go

keep in mind, that i starts with 0, so if you want to raise it, you have to do it like

zIndex: i + 1

here is the jquery

$(function() {
$(".book").each(function() {
    $(this).find("div").each(function(i) {
        if ($(this).is(".flipped")) {
            $(this).css({
                zIndex: i
            });
        } else {
            $(this).css({
                zIndex: $(this).parent().find("div").length - i
            });
        }
    });
});
});

http://jsfiddle.net/honk1/L8E8a/1/

looking for fewer lines?

$(".book").each(function() {
    $(this).find("div").each(function(i) {
        var level = $(this).is(".flipped") ? i : ($(this).parent().find("div").length - 1) - i;
        $(this).css("zIndex", level);
    });
});

http://jsfiddle.net/honk1/L8E8a/3/

EDIT

hold on a second:

$(".book").each(function() {
    $(this).find("div").each(function(i) {
        $(this).css("zIndex", $(this).is(".flipped") ? (i + 1) : $(this).parent().find("div").length - i);
    });
});

http://jsfiddle.net/honk1/L8E8a/5/

:)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top