On the page, there are 3 blocks (header, main and footer). 4th (apple_ios_status_bar_background) is hidden by default and displayed (or hidden) dynamically in code. When this unit is not displayed, you can see all 3 blocks on the page. If the display 4th block - the block footer goes down the page. It is necessary that would block main changed its height dynamically (all blocks should always be visible on the page).
Code https://jsfiddle.net/j3qm5qgx/1/
In JS detect iOS system, if true - show apple_ios_status_bar_background block, hide if false.
In your fiddle you did not include jQuery and second you did not define iOS. If you do so it works as you wanted it to.
var iOS = (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false);
https://jsfiddle.net/j3qm5qgx/4/
Note that Safari does not really mean iOS and that you could solve that issue in css with media device.
If you do not want your footer to go offscreen, you could set it on bottom via css:
footer {
position: absolute;
bottom: 0px;
height: 20px;
width: 100%;
background-color: #dff0d8;
}
https://jsfiddle.net/j3qm5qgx/5/
Another way would be to change it by jquery, since main now is 20 pixels more short:
if (iOS) {
$("#apple_ios_status_bar_background").show();
$('main')[0].style.height = 'calc(100% - (40px + 20px + 20px))';
} else {
$("#apple_ios_status_bar_background").hide();
}
https://jsfiddle.net/j3qm5qgx/6/
Related
I'm building a chat interface for a website. When it first loads, the most recent messages are displayed, with the newest at the bottom, as is common with most chat apps.
As the user scrolls up, more messages are loaded via our API, and inserted above the existing messages.
As you might expect, the chat bubbles are styled <div>s inside a container <div> that has overflow-y: auto and a set height.
Currently what I'm doing is noting the top message <div>, loading the older messages above it, and then repositioning the view to try and put the user back to almost where they left off, but it's tricky, especially when the chat bubbles contain dynamically loading elements (embedded images, etc). (I do not know the height of these bubbles before the browser renders them.)
In an ideal world, I would like to find a way to insert the messages above, without causing the scrolled position to move at all, so that the user doesn't lose track of where they are in the stream of messages.
I've never heard of such a thing, but it would be cool if there was a way to tell the div to stretch itself in the upward direction, rather than causing it to push the existing messages down further.
Any advice would be appreciated.
Here is a JSFiddle that shows the basic problem. It simulates my chat interface, by loading 30 chat messages when the page loads, and then puts you at the bottom of the message list. As you scroll to the top, another 30 are loaded, but you'll see that this is jarring to the UX because you instantly lose your place.
UPDATE: Thanks to Richard's answer, with a technique based on requestAnimationFrame to re-align the viewport. (For my use of it, I utilized the scrollTop of my scrollable div, rather than window.scrollTo as shown in his example.)
Fortunately, there is. The CSS property is called overflow-anchor. When you set the overflow-anchor property of an element to auto, it will turn on scroll anchoring; which is an attempt of the browser to minimize the problem of content jumping (such as a large image loaded above your scroll position causing your content to scroll further down); and set said element as the potential anchor when adjusting scroll position.
That being said, overflow-anchor is automatically set to auto and you needn't set it, so I'm not sure how setting it manually helped you. From MDN:
Scroll anchoring behavior is enabled by default in any browser that supports it. Therefore, changing the value of this property is typically only required if you are experiencing problems with scroll anchoring in a document or part of a document and need to turn the behavior off.
Here's a simple demonstration. At first, the window is already scrolled by 200px. Every two seconds, a new div is inserted on top of the container. Note that the visible area of the window doesn't change even when a new div is inserted on top the container.
const container = document.querySelector('#container')
window.scrollBy(0, 200)
let counter = 9
setInterval(() => {
let newDiv = document.createElement('div')
newDiv.classList.add('messages')
newDiv.innerText = counter++;
container.prepend(newDiv)
}, 2000)
* {
box-sizing: border-box;
font-family: Helvetica;
}
html,
body {
margin: 0;
}
#container {
width: 100%;
height: 100%;
padding: 0 20px;
scroll-behavior: smooth;
overflow-anchor: auto;
}
.messages {
width: 100%;
padding: 20px;
text-align: center;
background: #121212;
color: white;
border-radius: 5px;
margin: 20px 0px;
}
<div id="container">
<div class="messages">8</div>
<div class="messages">7</div>
<div class="messages">6</div>
<div class="messages">5</div>
<div class="messages">4</div>
<div class="messages">3</div>
<div class="messages">2</div>
<div class="messages">1</div>
</div>
Update
As setting overflow-anchor manually did help you with adjusting scroll position upon height increase above the viewport, but Safari does not support overflow-anchor, here's a simple alternative mechanism (using requestAnimationFrame) to adjust the viewport so that it always stays in its current position. This code below also adjusts the scroll position when you are at the top of the container and older messages show up (an issue you mentioned).
Note that I've set the overflow-anchor to none, thus disabling the scroll-anchoring browsers usually enable automatically, to show that this code works (I'm running it in Firefox). Also, the code is adjusted such that it does not scroll down when new messages are appended to the container. Do consider the scenario when the user is reading the last message and a new message gets sent; you might want to scroll down in that case. Furthermore, I am calling getBoundingClientRect() every frame. If not used properly, this can cause layout thrashing (also read here for the list of JS properties/methods that can cause this).
One last note: I actually wanted to use ResizeObserver, but as Safari does not support that API, I used rAQ instead to observe container's height changes. There are already ResizeObserver polyfills, which I have not tried out yet, but may be more efficient than using rAQ to observe height changes. Here's the page that contains a lot of links to ResizeObserver polyfills.
const container = document.querySelector('#container')
let counter = 9
setInterval(() => {
let newDiv = document.createElement('div')
newDiv.classList.add('messages')
newDiv.innerText = counter++;
container.prepend(newDiv)
}, 2000)
window.scrollBy(0, 200)
// Mimicking scroll anchoring behaviour from browser
let previousLastChild = container.lastElementChild
let previousBoundingRect = container.getBoundingClientRect()
function scrollAdjustment() {
let boundingRect = container.getBoundingClientRect()
let isHeightIncreased = boundingRect.height !== previousBoundingRect.height
let isAppend = container.lastElementChild !== previousLastChild // Is height increase caused by new messages being appended?
if (isHeightIncreased && !isAppend) { // If new messages are appended, don't scroll down as people are reading the upper messages
let newScrollYPosition = window.scrollY + boundingRect.height - previousBoundingRect.height
previousBoundingRect = boundingRect
window.scrollTo(0, newScrollYPosition)
}
requestAnimationFrame(scrollAdjustment)
}
requestAnimationFrame(scrollAdjustment)
* {
box-sizing: border-box;
font-family: Helvetica;
}
html,
body {
margin: 0;
}
#container {
width: 100%;
height: 100%;
padding: 0 20px;
scroll-behavior: smooth;
overflow-anchor: none; /* Note I've set it to none */
}
.messages {
width: 100%;
padding: 20px;
text-align: center;
background: #121212;
color: white;
border-radius: 5px;
margin: 20px 0px;
}
<div id="container">
<div class="messages">8</div>
<div class="messages">7</div>
<div class="messages">6</div>
<div class="messages">5</div>
<div class="messages">4</div>
<div class="messages">3</div>
<div class="messages">2</div>
<div class="messages">1</div>
</div>
Having this strange issue where page/screen is being cut off.
Seems to happen when you click into this screen below (which is shorter) then click back and go into another longer screen. The scrollbar doesn't appear so the page is cut off. Testing in Chrome v36 & iOS Safari.
http://onegreenthing.net/sunsuper/RetirementModeller/Forecast.html#retirement-age
Any ideas?
PS: Resize window so it's mobile like width (e.g. 400px wide)
.the-thing:before {
content: "";
display: block;
height: 0;
padding-top: 56.25%;
}
Smells like psuedo-vertical-centering that isn't recalculated to vanish appropriately if the page's content is naturally taller than the viewport.
Post-comment update - try this:
if( $(document).height() > $(window).height() ) {
$(".the-thing").remove();
}
Post-comment#2 - your main container element has this css (for id='retirement-age'):
.ui-mobile .ui-page {
min-height: 300px;
}
if I pull up my window shorter than 300 - I get the no-scroll thing - not buggy - that's correct.
Ok rookie error :( ..there was page with duplicate id, removed it and fixed the issue.
I have a fullscreen class which (obviously) makes any panel it is added to full screen. The scss is the following :
.fullscreen-div {
top: $navbar_height;
right: 0;
bottom: 0;
position: fixed;
z-index: 5;
padding: 0px 10px;
background: white;
}
Whenever an other specific panel is open however i want the full screen to be a bit smaller so the other panel can fit too. I try to do this with the following javascript :
function setWindowDimensions(){
...
//Fullscreen width
var fullscreen = $('.fullscreen-div');
if(leftPanelPresence){
fullscreen.css("left", leftPanelWidth);
}else{
fullscreen.css("left", 0);
}
}
This code is correctly run when the left panel is visible however the "left" style attribute fullscreen div only changes when i run this code twice. The first time it just stays the default left: 0; .
I checked if the jQuery element was correctly selected, id the leftPanelPresence was true and if the leftPanelWidth was set correctly which all seems to be the case.
The fullscreen button does the following (coffeescript):
$(document).on 'click', '.evidence-header h4 i.icon-resize-full', ->
evidenceView.addClass('fullscreen-div')
setWindowDimensions()
leftPanelWidth is set to 200 by default.
The .css jQuery line is run but i just don't see the change reflected in my browser. When i inspect the css in the browser it stays 0.
the argument of .css must be a string as you would write it in .css file
you need to add 'px' at the end leftPanelWidth.
It works with your default value because 0 is interpreted as a null value and works without the 'px'
any idea why in the example below, media queries stops changing the height of the menu bar after it's been changed by js? (make window small and click on the arrow to expand the mini menu). Do I need to register a point of origin for the menu element or something?
CSS:
#menu {
position: fixed;
left: 0px;
bottom: 0px;
width: 100%;
height: 90px;
z-index: 11000;
opacity: 1;
background-color: #F03600;
}
JS:
if ($("#arrowup").css('top') == '0px') {
$("#menu").animate({'height':'270px'}, 800, "easeInOutQuint");
} else {
$("#menu").animate({'height':'55px'}, 800, "easeInOutQuint");
}
You can check out the page here, all the code's on a single page:
http://www.nioute.co.uk/stuff/
Also, what's a good read with regards to media queries / js interaction?
Thanks!
The reason the media queries don't work is because when you modify the bar with Javascript, it applies inline-css. This overrides CSS that you may have in your stylesheets. The problem seems to be, when you toggle the arrow back down, #menu has an inline style of height="55px" applied to it, which blocks the regular style of 90px on a larger size.
The solution would be to clear the style when the window is resized to larger than your media query breakpoint using something like $(window).resize(function()...); and checking the current width of the window against your breakpoint. If it returns true, call $('#menu').attr('style', ''); and that will remove the inline style.
You can use class for adding some styles to elements and removing they after the job instead of getElementById(#menu).style.height = ...
for example:
getElementById(#menu).classList.add("newHeight")
Or
getElementById(#menu).classList.remove("newHeight")
Here's a link to what I'll be referring to.
I'm having some trouble getting the background image to work the way I'd like it to.
I want the background to auto resize based on the width of the window, which it is already doing correctly. If you make your window smaller you'll see the background shrink with it.
Here's the issue. If you make your window wide (short) then the background will resize and go too high so you can't see the top of the background anymore (since the background is bottom positioned).
I want the background to be top position when you are at the top of the page, and as you scroll down it will slowly move to be bottom positioned. Sort of like the effect of an Android phone's background when you move left and right. Of course, keep in mind that I still want the background to auto-resize when you make the window smaller.
html {
background-color: #70d4e3;
height: 100%;
}
body {
height: 100%;
}
.background {
margin-top: 45px;
width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: -9999;
}
.banner {
margin: 0px auto;
width: 991px;
margin-bottom: -9px;
}
.content {
background: url("http://i.imgur.com/daRJl.png") no-repeat scroll center center transparent;
height: 889px;
margin: 0 auto;
width: 869px;
}
.innerContent {
padding: 30px;
}
<img src="http://i.imgur.com/6d5Cm.jpg" alt="" class="background" />
<div class="banner">
<img src="http://i.imgur.com/JptsZ.jpg" alt="" />
</div>
<div class="content">
<div class="innerContent">
testing
</div>
</div>
Maybe some javascript or jquery would be needed to achieve this.
Well, this was fun, thanks!
I hope you don't mind me taking the liberty to use percentages to make my life a little bit easier and possibly the script slightly more robust since I can reliably use floats with percentages.
What I did is make the layout, html and css comply with the rules you need for the bg to be animated properly, they stayed largely the same from what you had.
Then it was just a question of figuring out the calculations needed with the right properties to figure out the percentage you were from the top, the *20 is actually the amount of space 'left' to fill by the background image in percentages (as the background height is 80%).
They I moved the calculations to a function so I could call that on scroll and on window resize, making sure it's initiated on any event that modifies the window somehow...
Didn't do extensive testing but it worked in Chrome and I'm tired :p
I believe this is what you are looking for:
http://jsfiddle.net/sg3s/RSqrw/15/ See edit 2
If you wanted this the other way arround just make the page background start at the top and modify that:
http://jsfiddle.net/sg3s/RSqrw/14/ See edit 2
Edit:
As a bonus, and since I had never actually written jquery script as a 'plugin', I decided to convert this into one. What I came up with should be easy to implement and use!
http://jsfiddle.net/sg3s/RSqrw/52/ See Edit 3
Functionality successfully tested in Chrome, Firefox 3.6, IE9 + compatibility mode
Edit 2:
Reading the question again checking if I did it right I noticed I didn't quite do what you want, so I updated the link in the first edit which gives you a plugin in which you can have several options for the scrolling background. It retains my 'old' interpetation while also doing what you want... Read comments in code for some extra descriptions.
Edit 3:
As I went to work today I was bothered with the fact that my plugin 'try' was a little bloated. And as you mentioned in the comment it didn't quite fit the requirements.
So I rewrote it to only do what you want and not much more, tested in Chrome Firefox, IE9 +compat etc etc.. This script is a lot cleaner.
http://jsfiddle.net/sg3s/vZxHW/
You can chose to make the background stick to the top or bottom if the height fits in the window. Nothing else, but that is already more than enough to do some pretty cool stuff :p
An exact solution: Fiddle: http://jsfiddle.net/srGHE/2/show/
View source
Thanks for the challenge. See below for the solution, which is complying with all requirements, including recommended yet optional (with steps on how to remove these) features. I only show the changed parts of your page, with an explanation after each section (CSS, HTML and JavaScript):
CSS (changes):
html,body{
margin: 0;
height: 100%;
padding: 0;
}
body{
background-color: #70d4e3;
}
#background { /*Previously: .background*/
/*Removed: margin-top: 45px;
No other changes*/
}
#banner /*Previously: .banner; no other changes */
#content /*Previously: .content; no other changes */
#innerContent /*Previously: .innerContent; no other changes */
Explanation of CSS revisions:
margin-top:45px at the background is unnecessary, since you're absolutely positioning the element.
All of the elements which are unlikely to appear more than once should be selected via the id (#) selector. This selector is more specific than the class selector.
HTML (changes):
All of the class attributes have been replaced by id. No other changes have been made. Don't forget to include the JQuery framework, because I've implemented your wishes using JQuery.
JavaScript (new):
Note: I have added a feature which you didn't request, but seems logical. The code will automatically reserve sufficient margin at the left side of the window in order to always display the background. Remove anything between the marked comments if you don't want this feature.
$(document).ready(function(){
//"Static" variables
var background = $("#background");
var marginTop = parseFloat(background.css("margin-top")) || 0;
var bannerWidth = $("#banner").width(); /*Part of auto left-margin */
var extraContWidth = (bannerWidth - $("#content").width())/2; /*Same as above*/
function fixBG(){
var bodyWidth = $("body").width();
var body_bg_width_ratio = bodyWidth/1920;
var bgHeight = body_bg_width_ratio * 926; //Calcs the visible height of BG
var height = $(document).height();
var docHeight = $(window).height();
var difHeight = bgHeight - docHeight;
var scrollDif = $(document).scrollTop() / (height - docHeight) || 0;
/*Start of automatic left-margin*/
var arrowWidth = body_bg_width_ratio * 115; //Arrow width
if(bodyWidth - bannerWidth > arrowWidth*2){
$("body > div").css("margin-left", "auto");
} else {
$("body > #banner").css("margin-left", arrowWidth+"px");
$("body > #content").css("margin-left", (arrowWidth+extraContWidth)+"px");
}
/*End of automatic left-margin*/
if(difHeight > 0){
background.css({top:(-scrollDif*difHeight-marginTop)+"px", bottom:""});
} else {
background.css({top:"", bottom:"0"});
}
}
$(window).resize(fixBG);
$(window).scroll(fixBG);
fixBG();
});
Explanation of the JavaScript code
The size of the background is determined by calculating the ratio of the background and document width. The width property is used, because it's the most reliable method for the calculation.
Then, the height of the viewport, document body and background is calculated. If applicable, the scrolling offset is also calculated, to prepare the movement of the background, if necessary.
Optionally, the code determines whether it's necessary to adjust the left margin (to keep the background visible at a narrow window).
Finally, if the background arrow has a greater height than the document's body, the background is moved accordingly, taking the scrolling position into account. The arrow starts at the top of the document, and will move up as the user scrolls (so that the bottom side of the arrow will be the bottom of the page when the user has fully scrolled down). If it's unnecessary to move the background, because it already suits well, the background will be positioned at the bottom of the page.
When the page has finished loading, this functionality is added to the Resize and scroll events, so that the background is always at the right location.
If you've got any other questions, feel free to ask them.
well, I'm not sure if I understand you and why do you want to do that, but you can try adding 2 backgrounds (see http://www.css3.info/preview/multiple-backgrounds/ ), one with the top bg and another with the bottom bg but I think that if the page is not too long it will cause issues, so the other answer with pure CSS is as follows: first add 3 horizontal divs with 100% width. Top div will have your top bg and its height, middle div will be transparent and auto height and bottom div will have your bottom bg and its height. All divs will have a 0 z-index. Then create a higher z-index div to act as a container and you'll be set. If I understand your question right, that's the close I can think of to achieve that. This being said, I'm pretty sure you can do this with JQuery with way better results
Using jQuery I was able to give you what I think you're asking for:
$(window).scroll(function() {
var h = Math.max($(document).height(), $(window).height());
var bottom = h - $(".background").height() - $(window).height();
$(".background").css("top", (($(window).scrollTop() / h) * bottom) + "px");
});
EDIT: Forgot to account for the way scrollTop reports position.
Or maybe:
.background {
margin-top: 45px;
max-width: 100%;
position: fixed;
bottom: 0;
left: 0;
z-index: -9999;
max-height: 100%;
}
I reccomend using jQuery Background Parallax
http://www.stevefenton.co.uk/Content/Jquery-Background-Parallax/
The function is as simple as
$("body").backgroundparallax();
Ask if you don't get it to work.
#abney; as i understand your question may that's you want http://jsfiddle.net/sandeep/RSqrw/60/
you need only css for this:
#background {
position: fixed;
width: 100%;
height:100%;
top: 0;
left:0;
z-index: -1;
}
The solution to your issue is a nice little lightweight plugin by Scott Robin. You can get more info, download it, and make your life easier for all of your projects by visiting his project page here.