I have a modal I position vertically like this:
method.center = function () {
var top, left;
top = Math.max($(window).height() - $modal.outerHeight(), 0) / 2;
left = Math.max($(window).width() - $modal.outerWidth(), 0) / 2;
$modal.css({
top:top + $(window).scrollTop(),
left:left + $(window).scrollLeft()
});
};
No matter the height of the window, Firefox is always + 157px on the top calculation. They both center horizontally fine. Is there a better, more consistent way to find the height of the window, subtract the height of the modal and divide by 2?
Are you really need to use JS calculation? Because it can achieve this with CSS only, like this:
.container {
border: 3px solid #73AD21;
display: flex;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.center {
margin: auto;
}
<div class="container">
<div class="center">
<b>Note: </b>Using margin:auto will not work in IE8, unless a !DOCTYPE is declared.
</div>
</div>
Related
I have a div that's 70% of the height of its' container/parent element. I want to expand this div to the very top as the user is scrolling downward within the div and vica versa ie the lowest the panel div can go is 70%. Is there an existing library for this? I need a smooth transition so getting the scrollTop property and setting the height (it is defined in percentage and not in pixel) of the panel div incrementally won't work.
Please find example here:
<div id='container'>
Scroll up to here
<div id='panel'>
As user scroll downs here in this div, expand this div to the top but only as much as the user has scrolled down: asdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd
</div>
</div>
#container {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: red;
}
#panel {
position: fixed;
width: 100%;
height: 70%;
bottom: 0;
background-color: green;
overflow-y: scroll;
}
https://jsfiddle.net/1ekpx3sd/
This is my proposed solution. The div's height is set via javascript as the user scrolls (by listening to the scroll event). Then, the height is set to 70% plus the amount the user has scrolled (by using the scrollTop property), up to a certain limit. The limit is the max possible height and is something that you must configure (I set it to 90% in my example, but it could also be a fixed amount):
function setHeight() {
var panel = document.getElementById("panel");
var container = document.getElementById("container");
var panelHeight = container.offsetHeight;
var y = panel.scrollTop;
// YOU MUST SET THE PERCENTAGE, OR A MAX HEIGHT
maxHeight = panelHeight * 0.9;
var targetHeight = Math.min(maxHeight, 0.7* panelHeight + y);
panel.style.height = targetHeight + "px";
document.getElementById("panelContent").style.marginTop = Math.min(y, panelHeight - maxHeight) + "px";
}
document.getElementById('panel').addEventListener("scroll", setHeight);
#container {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: red;
}
#panel {
position: fixed;
width: 100%;
height: 70%;
bottom: 0;
background-color: green;
overflow-y: scroll;
}
#panelContent {
}
<div id='container'>
Scroll up to here
<div id='panel'>
<div id='panelContent'>
As user scroll downs here in this div, expand this div to the top but only as much as the user has scrolled down: asdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasda<br>sdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd
</div>
</div>
</div>
I'm trying to center and scale an image inside a container. In the following diagrams, the pink box is a 16:9 container and the blue box is the image.
If the image is wider than the container, it will scale to fit.
If the image is taller than the container, it will also scale to fit.
If the image fits in the container, it will simply be centered.
As you can see in the diagrams, there is also a caption div aligned to the bottom left of the image, and a close icon aligned to the top right.
Here is the code I have now:
/* The page */
.container {
width: 100%;
height: 100%;
}
/* 16:9 container */
.imageWrapper {
padding-bottom: 56.25%;
position: relative;
width: 100%;
border: 1px solid red;
}
.imageInnerWrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
}
/* The image, footer and close button all need to fit inside the container */
.imageAndFooterWrapper {
display: inline-flex;
flex-direction: column;
position: relative;
border: 1px solid lightblue;
}
.image {
max-height: 100%;
object-fit: contain;
}
.footer {
text-align: left;
}
.closeButton {
position: absolute;
top: -30px;
right: -30px;
}
<div class="container">
<div class="imageWrapper">
<div class="imageInnerWrapper">
<div class="imageAndFooterWrapper">
<img class="image" src="https://images.theconversation.com/files/204986/original/file-20180206-14104-1hyhea9.jpg?ixlib=rb-1.1.0&rect=0%2C1212%2C5550%2C2775&q=45&auto=format&w=1356&h=668&fit=crop">
<div class="footer">
Caption
</div>
<div class="closeButton">X</div>
</div>
</div>
</div>
</div>
The following CodePen contains the above code, with some examples of different sized images.
https://codepen.io/anon/pen/NMKxxm
This may not be a direct answer to your question but usually when I have to do something like this, I use a div with a background image instead of an img tag. Using a div with a bg image allows you to use styles like background-image, background-position and background-size which allow you to create the effect as described by you.
Sample:
var imgDiv = $('.image')[0];
var closeButton = $('.fixed-el')[0];
var img = document.createElement('img');
img.src = getComputedStyle(imgDiv).backgroundImage.split('"')[1];
var calculate_positions = {
img_width: img.naturalWidth,
img_height: img.naturalHeight,
img_ratio: function() {
return calculate_positions.img_width / calculate_positions.img_height;
},
elm_ratio: function(elm) {
return $(elm).width() / $(elm).height();
},
img_offset: function(elm) {
var offset = []; //[x,y]
if (calculate_positions.elm_ratio(elm) > calculate_positions.img_ratio()) {
//centered x height 100%
var scale_percent = $(elm).height() / calculate_positions.img_height;
var scaled_width = calculate_positions.img_width * scale_percent;
var x_offset = ($(elm).width() - scaled_width) / 2;
offset = [x_offset, 0];
} else {
//centered y width 100%
var scale_percent = $(elm).width() / calculate_positions.img_width;
var scaled_height = calculate_positions.img_height * scale_percent;
var y_offset = ($(elm).height() - scaled_height) / 2;
offset = [0, y_offset];
}
return offset;
}
}
function updatePosition() {
var offset = calculate_positions.img_offset($('div.image'));
closeButton.style.top = offset[1] + 'px';
closeButton.style.left = offset[0] + 'px';
}
$(window).resize(updatePosition)
$(img).load(function() {
updatePosition();
});
div.image {
width: 100%;
background-image: url('http://via.placeholder.com/100x100');
background-position: center;
background-size: contain;
background-repeat: no-repeat;
flex: 1;
}
html,
body,
div.container {
width: 100%;
height: 100%
}
div.container {
display: flex;
position: relative;
flex-direction: column;
}
div.fixed-el {
position: absolute;
width: 20px;
height: 20px;
background: red;
}
div.caption {
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="image"></div>
<div class="caption">Some caption here</div>
<div class="fixed-el"></div>
</div>
EDIT:
You can change the image size in the styles and resize the window to see the scaling in action.
I also noticed the comment which mentioned that you do not want to use background image as it will clip the image. This will not happen if you use background-size:contain
EDIT 2:
It turns out you can actually figure out what the coordinates of the image are and position other elements around it. Have created a dirty hack to demonstrate this (mixed jQuery and vanilla JS, no proper scoping etc).. But you should be able to get the main idea and implement a neater solution. The main idea is derived from this question on SO
I have a somewhat strange behaviour in Chrome and Safari. I have a scaled (transform: scale()) container with a video and other elements inside of it. At some scalings the absolute positioned elements with a high z-index disappears and does not come back again.
How can I fix this?
Note that I cannot give the video element a negative z-index and I need to use overflow: hidden;.
Example
I have made an example that scales the outermost container up and down. At a specifik scale value the element with class .on-top (and text "I should always be on top.") disappears. When scaling down again it suddenly appears.
Link to exmaple: https://jsfiddle.net/iafiawik/Lcox1ecc/
Conclusions
It seems like the size of the element matters. The larger I make it, the larger is the scale value before it disappears.
I have also tested to set transform: scale(1.4) with CSS directly on the element and the behaviour is the same.
The issue does not exist if I:
Replace the video tag with a div
Remove position: absolute; from siblings to .on-top (that is, .below)
Remove overflow: hidden; from .content
If I move .on-top so it is placed after the video tag in the document flow
(But of course none of these workarounds work for me in reality because of project specific reasons. I also cannot give the video element a negative z-index and I need to use overflow: hidden;.)
Suggested workarounds from the community (thanks!)
Give the video tag a negative z-index (can't do this because I sometimes have elements placed behind the video)
Remove overflow: hidden; (I can't remove overflow: hidden;)
Browsers
I have seen this issue in Chrome (Mac) and Safari (Mac).
Update 1
Seems like this bug report pretty much covers my problem. However, it does not provide a fix for it.
Update 2
I've answered my own question by providing my solution to this problem.
Update 3
There are a lot of answers coming in that either modify the z-index of the video or adds translateZ to the .on-top element. Demos have shown that both of those approaches do fix the issue.
However, since my HTML structure is the output from a visual HTML editor (long story ...), I do not know what elements will be there or if they should be in front, below or next to a video. Therefore I am looking for a solution that does not require changes to individual elements that are inside the scaled element.
It looks like a bug in Chrome. Notice that when you scale the image, the element inspector keeps telling you that the size of #scaled is 1024x768:
Where as in Firefox:
Now, apparently, Chrome uses the wrong size to conclude that .on-top is completely outside .content and hides it because of hidden overflow (it should not be doing this but apparently it is trying to optimize away any element that displays above a video). Examples:
Scale: 1.225
Parent width: 1254.40
Child left: 1254.40 - (100 + 90) * 1.225 = 1021.65
Result: less than 1024 (partially inside)
Scale: 1.230
Parent width: 1259.52
Child left: 1259.52 - (100 + 90) * 1.230 = 1025.82
Result: greater than 1024 (completely outside)
Unfortunately I could not find an elegant solution. Ideally you should revise your HTML markup and CSS, perhaps align the top element with left edge. As a last resort, you can move the elements more towards left using transparent border:
var goalScale = 140;
var startScale = 100;
var currentScale = 100;
var shouldScaleUp = true;
var container = document.getElementById("scaled");
var scaleInfo = document.getElementById("scale-info");
function step() {
container.style.transform = "scale(" + (currentScale / 100) + ")";
scaleInfo.innerText = "Scale: " + (currentScale / 100);
if (currentScale === goalScale) {
shouldScaleUp = false;
}
if (currentScale === startScale) {
shouldScaleUp = true;
}
if (shouldScaleUp) {
currentScale += 0.5;
} else {
currentScale -= 0.5;
}
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
.scale-info {
position: fixed;
left: 0;
top: 0;
}
#scaled {
background: #cccccc;
width: 1024px;
height: 768px;
position: fixed;
left: 200px;
top: 200px;
}
.content {
height: 100%;
overflow: hidden;
position: relative;
margin-left: auto;
margin-right: auto;
background: rgba(34, 34, 56, 0.2);
}
.below {
position: absolute;
width: 300px;
height: 300px;
right: 0px;
top: 100px;
background: purple;
z-index: 1;
opacity: 0.8;
}
.below-2 {
z-index: 3;
right: 100px;
}
.below-3 {
z-index: 4;
right: 400px;
}
.on-top {
position: absolute;
width: 50px;
right: 100px;
top: 150px;
background: pink;
z-index: 5;
padding: 20px;
/* a 200px border moves the element towards left */
border-left: 200px solid transparent;
background-clip: padding-box;
}
.on-top h1 {
font-size: 20px;
}
#video {
position: absolute;
z-index: 4;
width: 1024px;
height: 768px;
background: rgba(0, 0, 0, 0.4);
}
<div id="scale-info"></div>
<div id="scaled">
<div class="content">
<h2 class="below below-1"> I have z-index 1</h2>
<div class="on-top">
<h1> I should always be on top.<br> I have z-index 5</h1>
</div>
<h2 class="below below-2"> I have z-index 3</h2> <video id="video" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
<h2 class="below below-3"> I have z-index 4</h2>
</div>
</div>
Here you go: https://jsfiddle.net/Lcox1ecc/423/
You just need to add -webkit-transform: translateZ(0); to the .on-top class.
Happy Coding!
After spending a lot of time researching this problem and trying a lot of different approaches I've come to the conclusion that no solution fixes my problem. There are solutions that fix the problem if you are able to control the z-indexes of the elements that disappear, but I am unable to do so since the structure of the HTML is not known to be (it is the output of the HTML editor). I was looking for a solution that would not require changes to individual children to the scaled parent, but I have not found any so far.
This bug report pretty much covers my problem but it does not provide a fix for it.
I can confirm that this happens because the element is outside of the scaled containers original width and height:
The element is visible at scale(1.227) (red border indicates the original size of #scaled):
... but not at scale(1.228):
My solution is therefore to add another wrapping element outside the scaled element that is not scaled, but get its width and height properties updated according to its first child scale values. This element has overflow: hidden; and prevents elements from being visible.
This is not a perfect solution as one might experience a small gap between the scaled element and the outermost wrapping element (rounding issues), but it is the best I can do given the circumstances.
var goalScale = 140;
var startScale = 100;
var currentScale = 100;
var shouldScaleUp = true;
var container = document.getElementById("scaled");
var scaledContainer = document.getElementById("resized-container");
var scaleInfo = document.getElementById("scale-info");
function step() {
var contentWidth = 1024;
var contentHeight = 768;
container.style.transform = "scale(" + (currentScale / 100) + ")";
scaledContainer.style.width = contentWidth * ((currentScale / 100)) + "px";
scaledContainer.style.height = contentHeight * ((currentScale / 100)) + "px";
scaleInfo.innerText = "Scale: " + (currentScale / 100);
if (currentScale === goalScale) {
shouldScaleUp = false;
}
if (currentScale === startScale) {
shouldScaleUp = true;
}
if (shouldScaleUp) {
currentScale += 0.5;
} else {
currentScale -= 0.5;
}
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
#resized-container {
position: fixed;
width: 1024px;
height: 768px;
overflow: hidden;
border: 10px solid red;
top: 200px;
left: 200px;
}
#scaled {
background: #cccccc;
width: 1024px;
height: 768px;
position: absolute;
transform-origin: left top;
}
.content {
height: 100%;
position: relative;
margin-left: auto;
margin-right: auto;
background: rgba(34, 34, 56, 0.2);
}
.below {
position: absolute;
width: 300px;
height: 300px;
right: 0px;
top: 100px;
background: purple;
z-index: 1;
opacity: 0.8;
}
.below-2 {
z-index: 3;
right: 100px;
}
.below-3 {
z-index: 4;
right: 400px;
}
.on-top {
position: absolute;
width: 50px;
right: -30px;
top: 150px;
background: pink;
z-index: 5;
padding: 20px;
}
.on-top h1 {
font-size: 20px;
}
#video {
position: absolute;
z-index: 4;
width: 1024px;
height: 768px;
background: rgba(0, 0, 0, 0.4);
}
<div id="resized-container">
<div id="scaled">
<div id="scale-info">
</div>
<div class="content">
<h2 class="below below-1">
I have z-index 1
</h2>
<div class="on-top">
<h1>
I should always be on top.<br /> I have z-index 5
</h1>
</div>
<h2 class="below below-2">
I have z-index 3
</h2>
<video id="video" src="https://www.w3schools.com/html/mov_bbb.mp4"></video>
<h2 class="below below-3">
I have z-index 4
</h2>
</div>
</div>
</div>
One approach, if you can modify a bit your html, is wrap your problematic elements in a container that is the same size as the video and container, with the proper z-index. That way you would have clear layers of the same size and positions, into which you can position more complex elements. Like this for example:
<div id="top-container">
<div class="on-top">
<h1>
I should always be on top.<br /> I have z-index 5
</h1>
</div>
</div>
#top-container {
width: 100%;
height: 100%;
position: absolute;
z-index: 5;
}
https://jsfiddle.net/06oykj8o/4/
I made this workaround by puttingz-index:-1; on video.
https://jsfiddle.net/Lcox1ecc/312/
I really like the answer from Salman A.
The only thing that comes to mind, would be rewriting with position: relative.
But I don't know if that is an option.
I stumbled across something similar to this last week with positioning absolute elements and transforms...
I dunno if this will help you out but here is a link.
CSS transform: translate moves postion:fixed inner Div
In the end I fixed it by using a transform: translateX(none) vs translateX(0).
Super strange behavior for sure, but the link gives some more links to help make things more clear - as in its behaving per spec.
It is happening because of overflow is hidden.Here is working link
https://jsfiddle.net/Lcox1ecc/322/
.content {
overflow:visible;
}
It might be late but just posting in case somebody finds it helpful.
Add an empty div under the parent container element with transform animation and nothing will disappear anymore. The animation does not do anything but it forces the browser to render all the elements using hardware acceleration.
<div class="emptydiv"></div>
.emptydiv{
transform:scale(1);
animation:fix 3s infinite;
}
#keyframes fix{
50%{
transform:scale(1);
}
}
I'm trying to figure out how Medium made their bottom action / menu bar slide up when your mouse enters the bottom of the document. The slide up effect is not triggered by moving the mouse over the invisible div (it slides up & down via transform translateY).
Besides, the menu bar is only 44px in height, but its is-visible class gets triggered way before your mouse is near it — but by what? When using Inspect Element, I can't see any hidden divs that could be triggering it..
I've searched for countless of ways, e.g. "show element when mouse enters specific part of document" but all search results involve when the mouse enters or moves over a div element, which is not the solution I'm looking for.
Obviously, you can solve this problem by putting the slide up menu inside a hidden container like I've done here, and then you get the desired result:
(function() {
var actionBar = document.querySelector('.action-bar');
var actionBarWrapper = document.querySelector('.action-bar-detection');
function showDiv() {
actionBar.classList.add('js-is-visible')
}
function hideDiv() {
actionBar.classList.remove('js-is-visible')
}
actionBarWrapper.onmouseover = showDiv;
actionBarWrapper.onmouseout = hideDiv;
})();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1.5;
}
body {
height: 100%;
}
.wrapper {
width: 90%;
max-width: 600px;
margin: 5% auto;
}
.action-bar {
position: absolute;
left: 0;
bottom: 0;
border: 1px solid #252321;
background: #fff;
padding: 16px;
width: 100%;
min-height: 50px;
opacity: 0;
transform: translateY(100%);
transition: all .5s;
z-index: 99;
}
.action-bar-detection {
height: 150px;
width: 100%;
opacity: 1;
position: fixed;
bottom: 0;
left: 0;
z-index: 1;
}
.js-is-visible {
opacity: 1;
transform: translateY(0%);
}
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div class="wrapper">
<p>When mouse enters the hidden action bar element, slides up.</p>
<p>But it's only happening because the action-bar is inside an invisible detection layer class (action-bar-detection) with a height of 150px.</p>
</div>
<div class="action-bar-detection">
<div class="action-bar">
Bottom Menu
</div>
</div>
</body>
</html>
However, this doesn't seem to be what Medium have done, and if this can be done without adding more HTML & CSS, I want to learn how! :-)
I think I'm not phrasing the problem correctly, since I can't find any solutions even remotely close (I've searched A LOT).
Any advice? What should I read up on? :-)
Get height of viewport, track onmousemove, and compare clientY from the mouse event to the viewport height:
(function() {
var actionBar = document.querySelector('.action-bar');
var viewHeight = window.innerHeight - 150;
function toggleDiv(e) {
if (e.clientY >= viewHeight) {
actionBar.classList.add('js-is-visible');
} else {
actionBar.classList.remove('js-is-visible');
}
}
window.onmousemove = toggleDiv;
})();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1.5;
}
body {
height: 100%;
}
.wrapper {
width: 90%;
max-width: 600px;
margin: 5% auto;
}
.action-bar {
position: absolute;
left: 0;
bottom: 0;
border: 1px solid #252321;
background: #fff;
padding: 16px;
width: 100%;
min-height: 50px;
opacity: 0;
transform: translateY(100%);
transition: all .5s;
z-index: 99;
}
.action-bar-detection {
height: 150px;
width: 100%;
opacity: 1;
position: fixed;
bottom: 0;
left: 0;
z-index: 1;
}
.js-is-visible {
opacity: 1;
transform: translateY(0%);
}
<div class="wrapper">
<p>When mouse comes within 150px of the bottom part of the screen, the bar slides up.</p>
<p>When the mouse leaves this defined area of the screen, the bar slides down.</p>
</div>
<div class="action-bar-detection">
<div class="action-bar">
Bottom Menu
</div>
</div>
You could do this by listening to the mousemove event on the document, you will want to invest effort into making this performant as it will be triggered frequently. The most common way to regulate events like this is through throttling.
Once you are hooked into the mousemove event you will need to get the Y coordinate of the cursor and compare that to the height of the window, if it is within a threshold then you can reveal your panel, once it moves out you can proceed to hide it again.
Here is an example showing a basic implementation jsFiddle
// Using underscore for the throttle function though you can implement your own if you wish
document.addEventListener('mousemove', _.throttle(mouseMoveEventAction, 200));
function mouseMoveEventAction(e) {
doPanelStuff(isInsideThreshold(e.clientY));
}
function doPanelStuff(isActive) {
var panelElement = document.querySelector('.panel');
if (isActive) {
panelElement.style.background = 'red';
} else {
panelElement.style.removeProperty('background');
}
}
function isInsideThreshold(cursorY) {
var threshold = 200;
var clientHeight = document.documentElement.clientHeight;
return cursorY > (clientHeight - threshold);
}
html, body {
height: 100%;
}
.container, .content {
height: 100%;
width: 100%;
}
.panel {
height: 50px;
position: absolute;
bottom: 0;
width: 100%;
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<div class="container">
<div class="content"></div>
<div class="panel"></div>
</div>
I have a div,I need to show this div on the center of the screen (ie viewable area) even while the user scrolled through the page.
so its style should be (for example)
{position:fixed; top:90px; left:150 px; z-index:9999; overflow:hidden;}
Now i need to find the value of left and top, so that the div will place in the center of the screen (ie viewable area), for any page .
How can i find the value of left & top with a javascript or jquery ?
left:50%; top:50%; puts you in the middle, then you apply a fixed width and height and set margin-top and margin-left to negative a half of that width and height.
In order to center something with fixed positioning then you will need to know the height and width of the element.
You then just need to margin-top and margin-left to negative half of the width and height in order to center it.
E.g. this class would center and element that has 100px height and 200px width.
.centered {
position: fixed;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -100px;
}
http://css-tricks.com/quick-css-trick-how-to-center-an-object-exactly-in-the-center/
Update:
If you don't know the height and width of the element that needs to be centered ahead of page load then you'll need to use JavaScript to detect the size.
Here's a working example of how this can be done - http://jsfiddle.net/3Ag97/1/
You can use the flexbox module, although it's not widely supported yet: http://jsfiddle.net/b7nrS/.
.container {
display: flex;
align-items: center;
justify-content: center;
}
Try this:
var $box = $('#box');
var bw = $box.width() / 2;
var bh = $box.height() / 2;
var wh = $(window).height() / 2;
var ww = $(window).width() / 2;
$box.css({'left': ww-bw, "top": wh-bh})
http://jsfiddle.net/mqMwj/
function centerObject(selector) {
var x = $(window).height() - $(selector).height();
var y = $(window).width() - $(selector).width();
$(selector).css("left", y / 2).css("top", x / 2);
};
#divX {
position: fixed;
left: 50%;
top: 50%;
width: 100px;
height: 100px;
margin-left: -50px;
margin-top: -50px;
background-color: #0000FF;
}