Creating a stack-view control with CSS/Javascript - javascript

I Want to create a stack type control where a series of stacked sub-divs can be re-ordered within a container div.
The sub-div 'views' would be the same size and absolutely positioned on top of each other.
The z-index css property seems to apply z-order at the document level, which makes no sense to me- How can an element in a given div have a higher z-order than something that is placed after that div? It just ignores the nested div heirarchy?
So the question is: Is there a way to manipulate relative z-order within a given div?
Thanks,
Yarin

How can an element in a given div have
a higher z-order than something that
is placed after that div? It just
ignores the nested div heirarchy?
Z-index only modifies the "layer" (imagine Photoshop) that the element is displayed on. Structurally, the box-model is not changed. Visually, it appears to be, but only if the positioning for the elements have been modified (through CSS) so that the z-index actually means something. Here's an example; notice how B appears above A and C event though C has the greatest z-index.
To modify the z-index of elements, relative to the container div that they are contained in, you have to make sure that the lowest z-index in the container is greater than the greatest z-index outside of the container. Then, you adjust the other z-indexes to offset them. This function (uses jQuery) gets the element with the greatest z-index, from the passed elements:
function getTopElement(elems){
// Store the greates z-index that has been seen so far
var maxZ = 0;
// Stores a reference to the element that has the greatest z-index so far
var maxElem;
elems.each(function(){
var z = parseInt($(this).css("z-index"), 10);
// Ignore z-index of auto
if (!isNaN(z)){
if (parseInt($(this).css("z-index"), 10) > maxZ) {
maxElem = $(this);
maxZ = parseInt($(this).css("z-index"), 10);
alert (maxZ);
}
}
});
return maxElem;
};
Use:
var topDiv = getTopElement($("div"));
var topZIndex = topDiv.css("z-index");
That should get you started.
Edit:
To fill in missing z-indexes, use this code:
var elements = $("div");
var newZ = parseInt(getTopElement(elements).css("z-index"), 10);
elements.each(function(){
if (isNaN(parseInt($(this).css("z-index"),10))){
$(this).css("z-index", ++newZ);
}
});
What it does is it changes elements with a z-index of auto to one-plus whatever the greatest z-index is in the collection (elements);
To see what I mean, check out this demo.

If you want only one div to be visible then change its opacity to 1 in Firefox and 100 in IE and set the others to 0. Here's a slide show example that does just that. I would imagine yours wouldn't be based on a timer so you would need some other method to switch them.

Related

Image resizes when position is changed to fixed on scroll

I would like an image to become fixed where it is once it appears on screen during a scroll, but become unfixed if the user scrolls back up.
However, when I use a JS on scroll function to change the image position to fixed, it suddenly "jumps"/resizes, and I'm not sure why. My fix was to create variables that alter the width and left values of the image after it becomes fixed, but I want to do this will multiple images on a page and each one seems to require a different width and left adjustment. I'm not sure what is conceptually causing this issue. Simply resetting the width and left to their original values does not work. Does a fixed position resize and image.
Here is a jsfiddle of the issue. And here is the code. JS:
var sitckyImageWidth = "38.4%";
var normalImageWidth = "48%";
document.addEventListener("scroll", function(){
var windowTop = $(window).scrollTop();
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
//loop through each div and grab top/bottom/midpoint info and find id
$('.articles').each(function(i){
var top = $(this).offset().top;
var bottom = top+ $(this).height();
var midPoint = (bottom+top)/2;
var thisId = this.id;
var newId;
//use container div info to find media info
var newId = thisId+"Media";
var sectionImage=document.getElementById(newId);
var sectionImageTop = $(sectionImage).offset().top;
//if article is on the page, change position to fixed and set top position
if (top<=windowTop&&bottom>windowTop){
$(sectionImage).css("top","10px")
$(sectionImage).css("position","fixed")
//$(sectionImage).css("width",sitckyImageWidth)
}
//if container is not at top of the page, return to relative
if (bottom<=windowTop||(bottom>windowTop&&top>windowTop)){
$(sectionImage).css("position","relative")
}
})
}); //end scroll
Position fixed means that an element will have relative sizes and positioning in relation to the viewport, not their parent elements.
Specifically what is happening here is that your div with ID article1Media is set to have a width of 48%. When it has the CSS property of position: relative then that resolves as 48% of the width of it's containing element (the div with id article1) however when it is position fixed that resolves as 48% of the width of the viewport. Since there is an implicit 8px margin on the <body> element then these are different.
There are a few different solutions to this, and how you tackle it depends on how you want to build your site.
Example fix 1
A simple fix for your immediate example is to simply add the following CSS.
body{
margin:0px;
}
.image{
margin:8px;
}
https://jsfiddle.net/Chipmo/k56qkk5b/13/
This moves the implicit margin onto the image element. Of course you can set it to whatever you like, or omit it entirely.
Look into CSS resets for more information about overriding implicit default styles, though be warned that it is possible to cause problems with overzealous reset codes.
( Edit: To be clear the above code is definitely not a drop in code for this problem everywhere. It will only work on quite simple HTML pages like your jsFiddle. )
Example fix 2
Another technique you could consider is locking the width and height to it's initial values using jQuery .width() and .height() functions. This would be inflexible, and you would have to do extra work to make it responsive (for mobile etc), but could be appropriate in some circumstances.
Example fix 3
A more portable solution might be to eschew relative sizing in favour of fixed widths and then use CSS media queries for reponsivity. Something like this:
.image{
width:500px;
}
#media(min-width:800px){
width:300px;
}
Example fix 4
If you wish to preserve the 'fluid' nature of using percentages I would suggest looking into using calc along with an offset that gets applied when the position is fixed (so, you add a class when you make the image fixed) that adjusts the sizing appropriately.
.media.image-fixed{
position:fixed;
width:calc(48% - 8px);
}
See this example https://jsfiddle.net/Chipmo/6mu2Lt9g/2/
Above behavior is observed since because of applying the position property not because of the scroll.Please take a look at the following link Position Properties
Position Fixed : Fixed position elements relative to document not on
any parent container.Hence occupies the complete width available.
Position Relative: Relative positioned elements behaves relative to hosted
containers.And inner elements consumes the hosted parent width.

Parallax effect on an elements position only when in view

Similar to this unresolved question (jQuery - parallax - update background position correctly)
I am animating the transform property of an element on page scroll to achieve a parallax-like effect. I want this element to only begin animating up when it is in view. The problem now is that if the element appears further down the page, it has already moved up a lot and loses the effect.
Here is my code currently
function parallax() {
var scrolled = $(window).scrollTop();
$('[data-scroll]').css('transform', 'translateY('+-(scrolled*0.02)+'px)');
}
$(window).scroll(function(e){
parallax();
});
In answer to your question how to separate "parallax'ed" divs, so they shift their position independently from each other upon scrolling, one should rely on their unique coordinates - each one has it's own $(elem).offset().top - a general vertical offset from the top of the page (it's stays the same all the time unless you meddle with the TOP property manually).
so all calculation could be based against this property.
$('.parallax').each(function(){
if ($(this).is_on_screen()) {
var firstTop = $(this).offset().top;
var winScrollTop = $(window).scrollTop();
var shiftDistance = (firstTop - winScrollTop)*0.02;
$(this).css("transform":"translateY("+shiftDistance+"px)");
}
});
plus you check if the element is in the viewport. Thus, you assure it moves the same delta distance in its own time no matter where it's on the page - further down or up.
Another thing is that how to put "borders" of visibility of the element on the screen. If you are moving an element when it's in viewport, i would suggest making a wrapping div within which the movement occurs (like a bg moving within a div wrapper).
<div class="parallax-section slide1">
<span class="moving-block"></span>
</div>
div has a bigger height and we check when this div is on the screen, not the moving element.
demo
Also other modifications can be applied if one needs different speed, offset for each element. I found this plugin a good beginner stuff to learn parallax.
P.S. btw, all initial properties should be cached in variables instead of retrieving them each time in a callback, like firstTop for instance

Convert Absolute Positioning Into Relative Positioning

Sorry if this is already out there somewhere, but I've been looking for a while and haven't found anything. I'd like to know if there's any way to, given the x and y coordinates/offset for an absolutely positioned element on a page, change that element into a relatively positioned element, but have it stay in the same visual spot, changing its location in the DOM. For example, given this html
<div id="divOne"></div>
<div id="divTwo"></div>
<div id="divThree"></div>
if divOne were positioned absolutely and its position happened to visually fall between divTwo and divThree, is there a way for me to convert its x,y position so I would be able to tell jQuery to place it after divOne and before divTwo in the DOM? I'm well versed in Javascript and jQuery, I'm just looking for a method I may not know about or for an answer from someone who may have come across this before.
by removing the absolute positioning of the dragged div you must place its tag to a different place in the dom in order to get it repositioned.
For example if you drag #divOne so that it visually moves between #divTwo and #divThree what you should do is remove the absolute positioning of #divOne and move its tag between the two other divs:
<div id="divTwo"></div>
<div id="divOne"></div>
<div id="divThree"></div>
If you have a well-defined grid this will work.
If I were you what I would do is to give a standard class to any div in my grid that I want be re-arrangeable. E.g., the class "arrangeable". When the drag-end fires I would calculate where my dragged div must be placed in the dom using this formula:
The dragged div should be moved to another place on the dom. What it will happen is that the dragged div will take the place of an existing one and "push" the existing just after it. For example by dragging #divOne between #divTwo and #divThree, #divOne takes the place of #divThree and pushes it after it. Supposing that the user stops the drag and releases left click when the dragged div is over the existing div whose place is going to be taken (let's name it "pushedDiv") by the dragged one then all you have to do is to recognize the pushedDiv, remove the dragged one from the dom, place right before the recognized one and make its position relative.
In order to realize which is the pushedDiv you can use this routine:
//on drag end, where mouse has x and y coords:
var pushedDiv;
$(".arrangeable").each(function(index,value){
var theoffset = $(this).offset();
if(x >= theoffset .left && x <= theoffset .left + $(this).width() && y >= theoffset .top && y <= theoffset .top + $(this).height()){
pushedDiv = $(this);
}
});
// if pushedDiv is still null then the user didn't release the left click over a div of class "arrangeable". Else, the pushedDiv will be the one we are looking for

jQuery offset, position element under another, where the right borders touch of the aligned elements

Ok, the subject is not super specific to the overall need I am looking to address. So I have this function that is called on a given element, which is a hidden submenu next to a triggering element. The way the page renders, and to keep the styling optimized I can't just style the position in via css. So I need javascript/jquerys help.
With that I have come up with a function that I can reuse as needed, and works fine all around. However my key problem is, that I have a couple cases where the submenu will overlap the edge of the <section> element it resides in. Which that elements overflow is set to hidden, that and its also about 30 pixels from the bottom of the page anyway. All in all the submenu element gets hidden a bit by either falling completely out of the pages view half way through the element, or it gets hidden by the section tags overflow state.
With that. In a case where this happens I am wanting to instead of have the element align to the bottom of the trigger element, have it align to the top instead so that way the menu in that case is above the trigger element and not below.
Problem is Im not sure how to compensate for that.
Here is the function I came up with to do what I need, now I just need some help in a sense catching when the menu element falls off the page so to speak, so I can adjust for it when it does.
function openSubOrgMenu(triggerID, elem)
{
orgSubOpenID = triggerID;
orgSubShowing = true;
elemOffset = elem.offset(); //trigger element
elemWidth = elem.width();
elemHeight = elem.height();
elemWrap = elem.siblings('.org_group_wrapper');//menu element
elemWrapWidth = elemWrap.width();
elemWrapHeight = elemWrap.height();
moveTop = elemHeight + elemOffset.top + 4;
moveLeft = elemOffset.left - (elemWrapWidth-elemWidth-15);
elemWrap.show().offset({top:moveTop, left:moveLeft});
}
Well here is a JS Fiddle, not necessarily showing a working logic of what I want, but demonstrating the desired effect when its an element that at the bottom of the page/section:
http://jsfiddle.net/4zwEr/

Anyone know how to get the z-index of a div tag that returns the actual number and not 'Auto'?

This gives me auto: I need the number, so I can move one above the other?
// Swop div index
$("#panel div").each(function(){
alert($(this).css('z-index'));
});
I don't think that browsers handle the z-index the way you seem to be expecting them to. For elements without a z-index, there just isn't a value there (which is basically what "auto" means).
Elements with a z-index of auto are laid out in the order they appear in the document tree.
If you want one on top, make its z-index equal to
the childNodes length of the parent.
(or one more than the largest z-index of any of the child nodes,
if there are any set).
Be sure to set the z-index as a string and not an integer,
and parseInt to compare any existing z-indexes.
try this
window.getZIndex = function (e) {
var z = window.document.defaultView.getComputedStyle(e).getPropertyValue('z-index');
if (isNaN(z)) return window.getZIndex(e.parentNode);
return z;
};
usage
var myZIndex = window.getZIndex($('#myelementid')[0]);
(if parent gets to root it will return 0)

Categories

Resources