I'm having trouble with the Slick carousel JS plugin with multiple slidesToShow which have different heights.
I need the Slides to have the same height, but with CSS flex-box it doesn't work as the slides have conflicting CSS definitions.
Also, I didn't find anything useful in the forums and on the web.
HTML
<div class="slider">
<div class="slide">
<p>Lorem ipsum.</p>
</div>
<div class="slide">
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua</p>
</div>
<div class="slide">
<p>At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
</div>
<div class="slide">
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr.</p>
</div>
</div>
JS
$('.slider')
.slick({
autoplay: false,
dots: false,
infinite: false,
arrows: false,
slidesToShow: 2,
slidesToScroll: 2,
rows: 0
});
CSS
.slide {
height: 100%;
background-color: #ccc;
padding: 10px;
}
Add a couple of CSS styles and it will be ready:
.slick-track
{
display: flex !important;
}
.slick-slide
{
height: inherit !important;
}
Enjoy! :-)
Ok guys i found an easy solution. Just add a setPosition callback function (fires after position/size changes) which sets the height of the slides to the height of the slider (slideTrack):
JS
$('.slider').slick({
autoplay: false,
dots: false,
infinite: false,
arrows: false,
slidesToShow: 2,
slidesToScroll: 2,
rows: 0
})
.on('setPosition', function (event, slick) {
slick.$slides.css('height', slick.$slideTrack.height() + 'px');
});
Dont forget that your slides need to have full height:
CSS
.slide {
height: 100%;
}
Here is a little jsFiddle to demonstrate:
https://jsfiddle.net/JJaun/o29a4q45/
The js solution from #JJaun is not perfect, because you see the height jumping if you use an background image for the slides. This worked for me:
.slick-track {
display: flex !important;
}
.slick-slide {
height: auto;
}
As answered above.works fine on slick slider
.slick-track
{
display: flex !important;
}
.slick-slide
{
height: inherit !important;
}
but, i have an issue when using slick sync navigation
simple put below css to cover it.
.slick-slide {
margin-bottom: 0;
object-fit: cover;
}
Here's an SCSS-only solution if you're OK with using object-fit:
.slick {
.slick-track {
display: flex;
.slick-slide {
display: flex;
height: auto;
img {
height: 100%;
object-fit: cover;
object-position: center;
}
}
}
}
I've another css-only solution. you can override floated elements with table/table-cell.
$(function() {
$('.slider')
.slick({
autoplay: false,
dots: false,
infinite: false,
arrows: false,
slidesToShow: 2,
slidesToScroll: 2,
rows: 0
});
})
.slide {
background-color: #ccc;
padding: 10px;
display: table-cell !important;
float: none !important;
}
.slick-track {
display: table !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"></script>
<div class="slider">
<div class="slide">
<p>Lorem ipsum.</p>
</div>
<div class="slide">
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua</p>
</div>
<div class="slide">
<p>At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
</div>
<div class="slide">
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr.</p>
</div>
</div>
For future searches:
You can simply use:
$('.slick').slick({
/* your config */
}).on('setPosition', function (event, slick) {
slick.$slides.css('height', slick.$slideTrack.height() + 'px');
});
.slick-track { display: flex; align-items: stretch; }
.slick-slide { height: auto; flex: 1; }
And, if you want to stick the last element in the bottom of the block, add display: flex for wrapper and margin-top: auto; to last element
Solution on pure css
.slick-track {
display: flex;
}
.slick-track .slick-slide {
display: flex;
height: auto;
}
.slick-slide img {
height: 100%;
object-fit: contain;
object-position: center;
}
I've wrote a quick JS hack to make a gallery with different images heights to look a little neater.
It does the following:
Get slider instance
Find out it's height - images height will be set to that
Get the src attr for each image and hide it
Set src attr to image's parent as a background image together with some CSS.
function equalizeImagesHeight(slider) {
const slides = slider.find('.slick-slide');
const imgHeight = $(slider)[0].clientHeight;
slides.each(function(slide){
const src = $(this).find('img').hide().attr('src');
$(this).css({
backgroundImage:'url('+src+')',
minHeight: imgHeight,
backgroundSize: "cover",
backgroundPosition: "center"
});
});
};
equalizeImagesHeight($('.my-slider'));
For me with latest version of slick in 2021
slick keep wrap my item's with additional div
So i do:
.slick-track
{
display: flex !important;
height: 100%;
}
.slick-slide
{
height: auto;
.slick-slide> div
{
height: 100%;
.myItemClass
{
height: 100%;
}
}
}
Above suggestions didn't work for me. My slider images are al portrait but might have different h/w aspect ratios. I fixed it using js. Feels ugly though, wish I found something cleaner.
$carousel.on('setPosition', function (event, slick) {
const $slideImages = slick.$slideTrack.find('.slick-slide-img');
if($slideImages.first()[0].clientHeight >= $slideImages.first()[0].naturalHeight) return;
$slideImages.height('auto');
$slideImages.width('100%');
const imgHeights = $slideImages.map(function(){
return this.clientHeight;
}).get();
const maxHeight = Math.max.apply(null, imgHeights);
$slideImages.height(maxHeight);
$slideImages.width('auto');
}
I had to use the setPosition event eventhough the code only needs to be executed once, after initialising slick. The init event doesn't work because the image heights on init are way off. Same for the first one or two setPosition events - hence the if($slideImages.first()[0].clientHeight >= $slideImages.first()[0].naturalHeight) return;.
Here is a jQuery approach to make the slides equal in height.
$('.slick-elem').on('init reInit', function(event, slick, currentSlide, nextSlide) {
setSlidesHeight(event);
});
function setSlidesHeight(event) {
let $ = jQuery;
let slickEl = $(event.target);
let slidesEl = slickEl.find('.slick-slide');
let maxHeight = -1;
slidesEl.each(function(index, el) {
maxHeight = maxHeight > $(el).height() ? maxHeight : $(el).height();
});
slidesEl.each(function(index, el) {
$(el).height(maxHeight);
});
}
Related
I recently started making a little website project and I'm struggling a bit with customizing my scrollbar.
I got so far that the scrollbar is only visible when you hover over it but that's not exactly my goal. I want it to be hidden when the user didn't scroll for a certain period of time. This is what I got so far:
<style>
::-webkit-scrollbar {
width: 6px;
height: 12px;
}
::-webkit-scrollbar-track {
background: rgba(242, 242, 242, 0);
}
::-webkit-scrollbar-thumb {
background: rgba(221, 221, 221, 0);
border-radius: 3px;
}
/*Commented because I don't want it to show when I just hover the site
body:hover::-webkit-scrollbar-thumb {
background: rgb(0, 0, 0);
}
*/
body.scrolling::-webkit-scrollbar-thumb,
::-webkit-scrollbar-thumb:horizontal:hover,
::-webkit-scrollbar-thumb:vertical:hover {
background: rgb(0, 0, 0);
}
::-webkit-scrollbar-thumb:horizontal:active,
::-webkit-scrollbar-thumb:vertical:active {
background: rgb(0, 0, 0);
}
</style>
<script>$(window).scroll(function() {
$('body').addClass('scrolling');
alert("!!");
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function() {
$('body').removeClass('scrolling');
}, 250));
});</script>
This is my first post on a forum like this so please just tell me if I have to provide more info and which info is missing.
I think its just a typo. Change the closing style tag to </style>. It can't be tested very well if theres an alert popping up every time you scroll. Remove alert("!!"); or change it to console.log("!!");
[LATER]
As you want the scrollbar to fade in and out with a transition, you'll have to use an element that covers it and animate its opacity. It's not possible to put an element above the document's scrollbar though. That's why you have to wrap the whole page inside a div and customize its scrollbar.
document.querySelector('.scroll-box').addEventListener('scroll', hideCoverBar);
document.querySelector('.scroll-box').addEventListener('mousemove', hideCoverBar);
var showTimeout;
function hideCoverBar() {
document.querySelector('.cover-bar').classList.add('hidden');
clearTimeout(showTimeout);
showTimeout = setTimeout(showCoverBar, 1000);
}
function showCoverBar() {
document.querySelector('.cover-bar').classList.remove('hidden');
}
body,
html {
margin: 0;
padding: 0;
font-family: monospace;
}
.main {
padding: 20px;
}
h1 {
font-size: 50px;
margin: 0;
}
p {
font-size: 12px;
margin: 0;
}
img {
display: block;
margin: 0 auto;
padding: 20px;
max-width: 100%;
box-sizing: border-box;
}
.scroll-bar-wrap {
width: 100vw;
position: relative;
}
.scroll-box {
width: 100%;
height: 100vh;
overflow-y: scroll;
}
.scroll-box::-webkit-scrollbar {
width: .4em;
}
.scroll-box::-webkit-scrollbar,
.scroll-box::-webkit-scrollbar-thumb {
overflow: visible;
border-radius: 4px;
}
.scroll-box::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, .2);
}
.cover-bar {
position: absolute;
background: #fff;
pointer-events: none;
height: 100%;
top: 0;
right: 0;
width: .4em;
-webkit-transition: all .5s;
opacity: 1;
}
.cover-bar.hidden {
opacity: 0;
}
<div class="scroll-bar-wrap">
<div class="scroll-box">
<div class="main">
<h1>Lorem ipsum dolor sit amet
</h1>
<img src="http://placekitten.com/600/400" />
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
</p>
</div>
</div>
<div class="cover-bar"></div>
</div>
fiddle: https://jsfiddle.net/71fjr0Lz/
I am trying to add an on-click-smooth-scroll effect like this one: https://michalsnik.github.io/aos/
I have read this: Smooth scroll to specific div on click and I am unable to adapt it. I don't understand what scrollTop: $("#page2").offset().top does.
My issue is that the scroll is "snapping". And that's probably because I have applied scroll-snap on the containers.
Also, when you're in-between the pages and click on the scroll down arrow it will either move up or down.
I would like to get the second page on full view whenever I press on that arrow. It should Scroll Down until #page2 has height: 100vh or it occupies the whole view port.
// eliminate scroll-bar
var child = document.getElementById('child-container');
child.style.right = child.clientWidth - child.offsetWidth + "px";
//scroll down effect on scroll-down-arrow
$(".scroll-down-arrow").click(function() {
$('html,body,#child-container').animate({scrollTop: $("#page2").offset().top}, 'slow', 'linear');
});
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* *** index.html - START *** */
body, html {
height: 100%;
width: 100%;
overflow: hidden;
}
#parent-container {
height: 100%;
width: 100%;
overflow: hidden;
position: relative;
}
#child-container {
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px; /* exact value is given through JavaScript */
overflow: auto;
scroll-snap-type: both proximity;
}
header {
height: 100%;
background-color: grey;
background-attachment: fixed;
background-position: bottom center;
background-repeat: no-repeat;
background-size: cover;
text-align: center;
scroll-snap-align: center;
}
header h1 {
font-size: 32px;
font-weight: bold;
position: sticky;
top: 5%;
margin-bottom:10px;
}
header p {
position: sticky;
width: 450px;
text-align: center;
margin: auto;
margin-top: 100px;
font-size: 1.5em;
}
header .scroll-down-arrow {
position: absolute;
left: 50%;
bottom: 20px;
display: block;
text-align: center;
font-size: 20px;
z-index: 100;
text-decoration: none;
text-shadow: 0;
width: 30px;
height: 30px;
border-bottom: 2px solid #fff;
border-right: 2px solid #fff;
left: 50%;
transform: translate(-50%, 0%) rotate(45deg);
animation: fade_move_down 3s ease-in-out infinite;
cursor: pointer;
}
/*animated scroll arrow animation*/
#keyframes fade_move_down {
0% { transform:translate(0,-15px) rotate(45deg); opacity: 0; }
25% {opacity: 1;}
/* 50% { opacity: 1; } */
100% { transform:translate(0,10px) rotate(45deg); opacity: 0; }
}
.container_page_2 {
width: 100%;
height: 100vh;
scroll-snap-align: center;
overflow: hidden;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<div id="parent-container">
<div id="child-container">
<!-- #header -->
<header>
<div class="nav-container">
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<h1 id="sticky-title">Lorem ipsum</h1>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Modi debitis in libero tenetur suscipit iusto eum nulla dolorum aperiam adipisci unde veritatis vel iure, a nam, saepe exercitationem illum vitae.</p>
<div class="scroll-down-arrow"></div>
</header>
<!-- #page2 -->
<div id="page2" class="container_page_2">
<div class="column active">
<div class="content">
<h1>1</h1>
<div class="box">
<h2>background-attachment: fixed;</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Inventore necessitatibus possimus fuga voluptate incidunt enim eius sed, ad suscipit error quasi ex blanditiis ipsa, at vero officiis voluptatem a modi!
</p>
</div>
</div>
<div class="bg bg1"></div>
</div>
<div class="column">
<div class="content">
<h1>2</h1>
<div class="box">
<h2>background-attachment: scroll;</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Inventore necessitatibus possimus fuga voluptate incidunt enim eius sed, ad suscipit error quasi ex blanditiis ipsa, at vero officiis voluptatem a modi!
</p>
</div>
</div>
<div class="bg bg2"></div>
</div>
<div class="column">
<div class="content">
<h1>3</h1>
<div class="box">
<h2>background-attachment: scroll;</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Inventore necessitatibus possimus fuga voluptate incidunt enim eius sed, ad suscipit error quasi ex blanditiis ipsa, at vero officiis voluptatem a modi!
</p>
</div>
</div>
<div class="bg bg3"></div>
</div>
<div class="column">
<div class="content">
<h1>4</h1>
<div class="box">
<h2>background-attachment: fixed;</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Inventore necessitatibus possimus fuga voluptate incidunt enim eius sed, ad suscipit error quasi ex blanditiis ipsa, at vero officiis voluptatem a modi!
</p>
</div>
</div>
<div class="bg bg4"></div>
</div>
</div>
https://codepen.io/bleah1/pen/gjYBgQ
I haven't added all the elements from the second page, but it doesn't matter, because the scrolling isn't affected. As you can see it's not smooth at all, it's actually pretty snappy.
What do you think ? I would like to keep the scroll-snap, because I like that idea.
Hi can you try this solution.
Basically I removed the css when click event starts then added it when the scrollTop event ends.
Remember to remove it from your css #child-container
$(".scroll-down-arrow").click(function() {
$('#child-container').css('scroll-snap-type','')
$('html,body,#child-container').animate({
scrollTop: $("#page2").offset().top}, 'slow', 'linear')
.promise()
.done(() => {$('#child-container')
.css('scroll-snap-type','both proximity')
});
});
Based on #Ekin Alcar answer I was able to fix my issue. I followed his idea of removing the scroll-snap-type css attribute from #child-container by using $('#child-container').css('scroll-snap-type',''); inside of the original script, like this:
$(".scroll-down-arrow-container").click(function() {
$('#child-container').css('scroll-snap-type','');
$('html, body, #child-container').animate({
scrollTop: $(window).height()
}, 1000)
.promise()
.done(() => {$('#child-container')
.css('scroll-snap-type','both proximity')
});
});
The trick with .css is that it can only remove attributes that are used in the style tag inside a .html file. It won't work with a .css stylesheet.
From the API's documentation:
It does not, however, remove a style that has been applied with a CSS rule in a stylesheet or < style > element.
As such scroll-snap-type: both proximity; was removed from the .css file and added in the .html file:
<div id="child-container" style="scroll-snap-type: both proximity;">
Also, to fix the effect of scrolling up or down whenever you're in between pages I've replaced scrollTop: $("#page2").offset().top with scrollTop: $(window).height(). Don't ask me why it works, but it does.
I have written my own modal classes using css and have used it in my application successfully. However the issue i'm facing is when the overlay is open i can still scroll the background contents. How can i stop scrolling background contents when my modal/overlay is open?
This is my modal which opens on top of the overlay
<div>
<div className="overlay"></div>
{this.props.openModal ?
<div>
<div className="polaroid sixten allcmnt_bg_clr horiz_center2">
{}
<div className="mobile_header">
<PostHeader/>
</div>
<div className="mobile_renderPost">
{ this.renderPostType() }
</div>
<div className="mobile_post_bottom"></div>
</div>
</div> : null}
</div>
my overlay css
.overlay {
background-color: rgba(0, 0, 0, .70);
position: fixed;
width: 100%;
height: 100%;
opacity: 1;
left: 0;
right: 0;
-webkit-transition: opacity .25s ease;
z-index: 1001;
margin: 0 auto;
}
One approach is hidden the overflow of the body element.
like this:
body.modal-open{
overflow:hidden;
}
so in this case when you popup the modal you add a class to body and then when you close it you remove that class.
another approach is using a javascript to disable the scroll like this:
document.documentElement.style.overflow = 'hidden';
document.body.scroll = "no";
and then return it with
document.documentElement.style.overflow = 'scroll';
document.body.scroll = "yes";
When you open the modal, you can add overflow: hidden; to the body's style.
Or,
body.modal-opened {
overflow: hidden;
}
And add modal-opened class to the body when opening and remove when you close the dialog.
Using JavaScript to add a class to the body with
overflow:hidden;
will work in most cases, but I beleive Safari on iPhone will still scroll slightly with jitter due to Touch Move and something like this will be needed.
function handleTouchMove(e)
{
e.preventDefault();
}
function lockscreen()
{
var body = document.getElementById("body");
body.className += " lock-screen";
body.addEventListener('touchmove', handleTouchMove, false);
}
function unlock()
{
var body = document.getElementById("body");
body.classList.remove("lock-screen");
body.removeEventListener('touchmove', handleTouchMove);
}
to stop the user from still scrolling
I had this problem too and tried every answer from setting the height on the body element to 100% or 100vh and overflow: hidden. This caused a few issues for me, starting with that using the hidden overflow with the 100vh made the page jump to the top whenever clicking the hamburger menu button.
The solution: adding the overflow:hidden property to the html tag. This worked perfectly where the menu would open, prevent the page from scrolling, and remain where the user is on the page without it jumping.
Since it looks like you're using React, here is an example of how I used it:
.lock-scroll {
overflow: hidden;
}
const [open, setOpen] = useState(false)
useEffect(() => {
const html = document.getElementsByTagName('html')[0]
if (open) {
html.classList.add('lock-scroll')
} else {
html.classList.remove('lock-scroll')
}
return (): void => {
html.classList.remove('lock-scroll')
}
}, [open])
When the modal opens, hide the x/y scroll bars on the body.
.no-scroll {
overflow: hidden;
}
Using JavaScript add the class to the body:
<body class="no-scroll">
</body>
Once you close the modal remove the class.
Combining the overflow: hidden solution with modern technique works perfectly
html:has(dialog[open]) {
/* remove the main scrollbar when dialog is open */
overflow: hidden;
}
All the answers so far (Oct. 2022) suggest to add overflow: hidden dynamically to either 'body' or 'html' when you open the modal/pop-up. This works if 'html' or 'body' are actually your scrolling elements and fixes the somewhat counterintuitive over-scroll behavior of position: fixed elements.
I've tried to use overscroll-behavior instead to fix the issue and this can work, but requires scrollable elements (with actual overflow) inside your modal and is not very reliable if the user simply decides to touch your overlay outside of the "locked" elements.
Depending on your page design there is another option that requires you to split content and overlay and set the 'html' and 'body' height explicitly to 100%. Here is a complete example:
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
box-sizing: border-box;
}
body {
position: relative;
margin: 0;
padding: 0;
font-family: sans-serif;
font-size: 16px;
}
.content {
padding: 32px;
border: 1px solid #000;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, .5);
backdrop-filter: blur(5px);
display: flex;
justify-content: center;
align-items: center;
z-index: 1001;
}
.info-box {
background: #000;
color: #eee;
border-radius: 5px;
width: 240px;
height: 240px;
padding: 16px;
}
/* scroll fix */
html, body {
height: 100%;
}
.content {
max-height: 100%;
overflow-y: auto;
}
</style>
</head>
<body>
<div class="content">
<h2>Overlay Background Scroll Test</h2>
<p>Use a window size of around 320x480 for optimal testing (e.g. via device-toolbar).</p>
<h3>Scrollable Page</h3>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
<p>At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
<button onclick="document.querySelector('.overlay').style.display='flex';">open</button>
</div>
<div class="overlay">
<div class="scroll-fix">
<div class="info-box">
<h3>Pop-Up Message</h3>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
<button onclick="document.querySelector('.overlay').style.display='none';">close</button>
</div>
</div>
</div>
</body>
The important section is the last CSS entry. This will make your .content element the main scroll element:
html, body {
height: 100%;
}
.content {
max-height: 100%;
overflow-y: auto;
}
There is a disadvantage to this approach though. Most mobile browsers will not be able to automatically hide their URL-bar through scrolling anymore, because this seems to depend on html or body element scrolling :-(.
I have several divs arranged vertically one on top of the other which from here on out I will call panels. Each panel has the width and height of the viewport. All panels have the same background color at all times. Initially, that color is black.
Every other panel is empty, and acts as a gap between panels that actually have content. The order is like this:
Content
No content
Content
No content
Content
What I want to do is make it so that when a user has scrolled down enough for a panel with content to be out of view, the color should change from black to white. Then, once they have scrolled far enough for the second panel with content to be out of view, it should change back.
This is the part I cannot figure out. I have a working demo with my code so far:
$(document).scroll(function() {
var viewportHeight = $("html").outerHeight();
var currentY = $(document).scrollTop();
if (currentY % viewportHeight != 0) {
lighten();
} else {
darken();
}
});
function darken() {
$("body").css("background-color", "black");
$(".panel.content").css("color", "white");
}
function lighten() {
$("body").css("background-color", "white");
$(".panel.content").css("color", "black");
}
html,
body,
.panel {
width: 100%;
height: 100%;
}
body {
background-color: black;
overflow-y: visible;
overflow-x: hidden;
transition: background-color 500ms linear;
}
.panel {
border: 2px solid red;
}
.content {
color: white;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<body>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
</body>
As you can see, my code is a ways off from achieving the desired result. Not only is the order wrong, the main problem is that it only triggers when the user is at the exact Y for the change to happen.
The order should be:
Black
White
White
Black
Black
White
White
Black
Black
etc.
How do I do this?
$(document).scroll(function() {
var viewportHeight = $("html").outerHeight();
var currentY = $(document).scrollTop();
var panelNumber = Math.floor(currentY / viewportHeight);
if (panelNumber % 4 === 1 || panelNumber % 4 === 2) {
lighten();
} else {
darken();
}
});
function darken() {
$("body").css("background-color", "black");
$(".panel.content").css("color", "white");
}
function lighten() {
$("body").css("background-color", "white");
$(".panel.content").css("color", "black");
}
html,
body,
.panel {
width: 100%;
height: 100%;
}
body {
background-color: black;
overflow-y: visible;
overflow-x: hidden;
transition: background-color 500ms linear;
}
.panel {
border: 2px solid red;
}
.content {
color: white;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
</body>
Not only is the order wrong, the main problem is that it only triggers
when the user is at the exact Y for the change to happen.
yes..that is right. currentY % viewportHeight != 0 will not work. Scrolling is not smooth. Pulling scrollbar down may result in change of 10000 px/sec, but browser renders 60fps which means your currentY (scroll-y) will increase by 150px every frame (your are expecting a change of 1px). So if your code contains if(currentY === 100){} definitely not going to work. currentY may have values like 0,50,80,99,120,....
It should be clear currentY % viewportHeight != x is not so much different.
So if(0<currentY<500) will be be better option.
Now suppose viewportHeight = 500. We can get panelNumber = Math.floor(currentY/viewportHeight) (=>0,1,2,3,4,5...)
(Not clear from your question) Suppose you want to have
panel: 0 =>black, 1=>white, 2=>white, 3=>black, 4=>black ...
you can get black by panelNumber%4==0 || panelNumber%4==3.
[please change accordingly if you want something different]
I know it's a bit late, but I made a solution for this and didn't had time to post it.
So: "How is my solution any different?"
I get gap-from-top values to all your .blank divs and then check where view-port is located. This allows you to use borders, add any other divs in between, change size of your divs and this will not lose functionality.
var elem = document.getElementsByClassName("blank");
var i;
var marks = [];
for (i = 0; i < elem.length; i++) {
var rect = elem[i].getBoundingClientRect();
marks.push(rect.top);
}
$(document).scroll(function() {
if (findi() % 2 == 1) {
lighten();
} else {
darken();
}
});
function findi() {
pos = 0;
for (i = 0; i < marks.length; i++) {
if (marks[i] < $(document).scrollTop()) {
pos++;
}
}
return (pos);
}
function darken() {
$("body").css("background-color", "black");
$(".panel.content").css("color", "white");
}
function lighten() {
$("body").css("background-color", "white");
$(".panel.content").css("color", "black");
}
body {
background-color: black;
overflow-y: visible;
overflow-x: hidden;
transition: background-color 500ms linear;
}
html,
body {
padding: 0;
margin: 0;
}
html,
body,
.panel {
width: 100%;
height: 100%;
}
.panel {
border-bottom: 32px solid red;
}
.content {
color: white;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
Here is my solution. I'm pretty sure this is the order you need.
(function() {
var lastfunc = darken;
$(document).scroll(function() {
var y = $(document).scrollTop();
var x = $("html").outerHeight();
var func = (Math.floor(((y + x) / (2 * x))) % 2) ? lighten : darken;
if (func !== lastfunc) {
lastfunc = func;
func();
}
});
})();
This does the following:
Create a variable lastfunc to keep track of the last thing we called.
Get the user's Y and the height of the viewport.
Use evil factorized math to get a bool which decides which of the two functions, lighten() or darken() to assign to func.
Check to see if lastfunc is equal to func, to see if a change is necessary.
If they are not equal, call func() and set lastfunc equal to func
This has the following benefits:
All wrapped up to prevent making lastfunc a global.
Math is solid.
This calls the style modifying functions only if a change is required.
I've also optimized your darken() and lighten() functions:
function darken() {
document.body.style.backgroundColor = "black";
$(".panel.content").css("color", "white");
}
function lighten() {
document.body.style.backgroundColor = "white";
$(".panel.content").css("color", "white");
}
This slight optimization saves jQuery the trouble of finding your body, which should save off a couple of milliseconds. Not a huge difference, but this is optimal.
Additional note: if you are able to get rid of the second style change in lighten and darken, we can get rid of the overhead of having the function calls at all, and do it all within scroll():
(function() {
var lastColor;
$(document).scroll(function() {
var y = $(document).scrollTop();
var color = (Math.floor(((y + x) / (2 * x))) % 2) ? "red" : "blue";
if (color !== lastColor) {
lastColor = color;
document.body.style.color = color;
}
});
})();
Demo of the first solution:
(function() {
var lastfunc = darken;
$(document).scroll(function() {
var y = $(document).scrollTop();
var x = $("html").outerHeight();
var func = (Math.floor(((y + x) / (2 * x))) % 2) ? lighten : darken;
if (func !== lastfunc) {
lastfunc = func;
func();
}
});
})();
function darken() {
document.body.style.backgroundColor = "black";
$(".panel.content").css("color", "white");
}
function lighten() {
document.body.style.backgroundColor = "white";
$(".panel.content").css("color", "white");
}
html,
body,
.panel {
width: 100%;
height: 100%;
}
body {
background-color: black;
overflow-y: visible;
overflow-x: hidden;
transition: background-color 500ms linear;
}
.panel {
border: 2px solid red;
}
.content {
color: white;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<body>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
</body>
You can use .hover() on $(document).ready(); I have added an example of it if it works for you.
$(document).ready(function() {
$(".panel.content").hover(function(){
$("body").css("background-color", "white");
$(this).css("color", "black");
},function(){
$("body").css("background-color", "black");
$(this).css("color", "white");
});
});
$(document).scroll(function() {
$(".panel.content").hover(function(){
$("body").css("background-color", "white");
$(this).css("color", "black");
},function(){
$("body").css("background-color", "black");
$(this).css("color", "white");
});
});
html,
body,
.panel {
width: 100%;
height: 100%;
}
body {
background-color: black;
overflow-y: visible;
overflow-x: hidden;
transition: background-color 500ms linear;
}
.panel {
border: 2px solid red;
}
.content {
color: white;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<body>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
</body>
I've made it so you can choose which panel sets the background color to white/black. You can do that by adding the class 'black' or 'white' to it, though the way you requested it makes perfect sense afterall.
Also my solution uses a class to change the background color of the body. This should make it more easy to make style changes.
$(document).ready(init)
function init(){
$(document).scroll(updateBackground)
updateBackground()
}
function updateBackground(){
var w = $(window).width()
var h = $(window).height()
var panels = $('.panel') // get all panels on page
for(var i=0;i<panels.length;i++){ // loop though each panel
var panel = panels.eq(i) // get the current panel
var panel_y = panel.offset().top - $(document).scrollTop() // get the panels y coordinate relative to the window
var panel_height = panel.height() // get the panels height
if(panel_y<=0 && panel_y+panel_height>0){ // check if the panel is in the visible area
if(panel.hasClass('black')){ // check if the panel is set to make the background black
$('body').removeClass('white')
$('body').addClass('black')
}else if(panel.hasClass('white')){
$('body').removeClass('black')
$('body').addClass('white')
}
return // return, because we already found the visible panel
}
}
}
html{
height: 100%
}
body{
margin: 0;
padding: 0;
transition: background-color 500ms linear;
height: 100%
}
body.white{
background-color: white;
color: black;
}
body.black{
background-color: black;
color: white;
}
.panel{
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 50px;
border: solid 1px #888888;
text-align: center;
overflow: hidden;
}
.panel > h1{
font-size: 38pt;
}
.panel > p{
font-size: 18pt;
line-height: 200%;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head
<body>
<div class="panel with-content black">
<h1>Content</h1>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
</div>
<div class="panel gap white"></div>
<div class="panel with-content white">
<h1>Content</h1>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
</div>
<div class="panel gap black"></div>
<div class="panel with-content black">
<h1>Content</h1>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
</div>
<div class="panel gap white"></div>
<div class="panel with-content white">
<h1>Content</h1>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
</div>
<div class="panel gap black"></div>
<div class="panel with-content black">
<h1>Content</h1>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
</div>
</body>
</html>
Add a condition in your code
Math.floor(currentY / viewportHeight) % 4 == 1 || Math.floor(currentY / viewportHeight) % 4 == 2
and your code is working now.
Testing
If you run this code in java it will display same output as you expected.
public static void main(String...arr){
for(int i = 0; i<100; i++){
if(i%4 ==1 || i%4 == 2){
System.out.println("true");
} else {
System.out.println("false");
}
}
}
$(document).scroll(function() {
var viewportHeight = $("html").outerHeight();
var currentY = $(document).scrollTop();
if (Math.floor(currentY / viewportHeight) % 4 == 1 || Math.floor(currentY / viewportHeight) % 4 == 2) {
lighten();
} else {
darken();
}
});
function darken() {
$("body").css("background-color", "black");
$(".panel.content").css("color", "white");
}
function lighten() {
$("body").css("background-color", "white");
$(".panel.content").css("color", "black");
}
html,
body,
.panel {
width: 100%;
height: 100%;
}
body {
background-color: black;
overflow-y: visible;
overflow-x: hidden;
transition: background-color 500ms linear;
}
.panel {
border: 2px solid red;
}
.content {
color: white;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
</body>
What I want to do is make it so that when a user has scrolled down enough for a panel with content to be out of view, the color should change from black to white. Then, once they have scrolled far enough for the second panel with content to be out of view, it should change back.
This statement conflicts with the order you mentioned. From the statement, it is implied that the color changes should be alternate, but the order mentioned in your question is not alternate.
Please see the below code which changes colors on alternate divs through out the scrolling.
$(document).scroll(function() {
var viewportHeight = $("html").outerHeight();
var currentY = $(document).scrollTop();
if (Math.floor(currentY / viewportHeight) % 2) {
lighten();
} else {
darken();
}
});
function darken() {
$("body").css("background-color", "black");
$(".panel.content").css("color", "white");
}
function lighten() {
$("body").css("background-color", "white");
$(".panel.content").css("color", "black");
}
html,
body,
.panel {
width: 100%;
height: 100%;
}
body {
background-color: black;
overflow-y: visible;
overflow-x: hidden;
transition: background-color 500ms linear;
}
.panel {
border: 2px solid red;
}
.content {
color: white;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<body>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
<div class="panel blank">
</div>
<div class="panel content">
<h2> CONTENT </h2>
</div>
</body>
I made DEMO with JSfiddle so please check.
This show a comment that slides from right side to very left.
It's shown just a little bit above than middle of vertical align.
How can I show it right in middle?
Please fix and update my JSfiddle
Javascript
function transition() {
$('.newsticker p').animate({"marginLeft":"400px","opacity":".0"}, 600).fadeOut(100);
$('.newsticker').append("<p style='margin-left:400px;opacity:0'>Hello! This is a test</p>");
$('.newsticker p').animate({"marginLeft":"0px","opacity":"1"}, 600);
}
setInterval(transition, 2000);
CSS
div.newsticker{
border:1px solid #666666;
width:100%;
height:100px;
}
.newsticker p{
padding-left:10px;
padding-right:10px;
float:left;
position:absolute;
}
HTML
<div class="newsticker">
</div>
First reset browsers default stylesheet like margin or padding by:
* {
padding: 0;
margin: 0;
}
Then add line-height: 100px; CSS declaration to the .newsticker element:
div.newsticker{
border:1px solid #666666;
width:100%;
height:100px; /* -------- */
line-height: 100px; /* | */
/* ^---------- */
}
JSFiddle Demo
Update
By using CSS, it is almost impossible to achieve this goal. I create a jQuery version, It calculates height of the dynamic paragraph and set top property to get it to the middle of its parent. In this case a little change is needed in CSS:
CSS:
div.newsticker {
position: relative; /* Add relative position to the parent */
overflow: hidden; /* Hide the overflow */
}
.newsticker p {
width: 100%; /* Set the width of paragraph to '100%' or 'inherit' */
}
JavaScript/jQuery:
var newsticker = $('.newsticker'),
maxHeight = newsticker.height();
function transition() {
newsticker.find('p').animate({
marginLeft : "400px",
opacity : ".0"
}, 600).fadeOut(100);
newsticker.append(
$('<p>').css({
'margin-left' : '400px',
'opacity' : '0'
// Put your text in .text() method:
}).text('Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsam suscipit nihil voluptatibus maxime sit quam delectus eaque officiis cumque accusamus velit nesciunt deserunt veniam molestias alias? Eaque iste quia non.')
).find('p').each(function() {
if ($(this).css('top') == 'auto')
$(this).css('top',
(maxHeight - $(this).height()) / 2
);
});
newsticker.find('p').animate({"marginLeft":"0px","opacity":"1"}, 600);
}
setInterval(transition, 2000);
Here is the JSFiddle Demo.
UPDATE
new Fiddle: New JsFiddle
HTML:
<div class="newsticker">
<div class="middle"><p><p></div>
</div>
JS:
function transition() {
$('.middle').animate({"right":"-100%","opacity":".0"}, 600, function() {
$('.middle').first().remove();
});
var width = $('.newsticker').width();
$('.newsticker').append("<div class='middle'><p style='width: " + width + "px;'>Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum</p></div>");
var height = $('.middle p').last().height() / 2;
$('.middle p').css('top','-' + height + 'px');
$('.middle').animate({"right":"0px","opacity":"1"}, 600);
}
setInterval(transition, 2000);
CSS:
div.newsticker{
border:1px solid #666666;
width:100%;
height:100px;
padding: 0px;
margin: 0px;
overflow: hidden;
position: relative;
}
.newsticker p{
padding-left:10px;
padding-right:10px;
line-height: 1em;
padding: 0px;
margin: 0px;
position: relative;
display: block;
}
.middle {position: absolute; top: 50%; padding:0; margin:0; right: -100%; opacity: 0;}
ORIGINAL ANSWER
here is the working fiddle
JsFiddle
you needed 100px line-height on your p tag and you needed to reset padding and margin on your div and p
div.newsticker{
border:1px solid #666666;
width:100%;
height:100px;
padding: 0px;
margin: 0px;
}
.newsticker p{
padding-left:10px;
padding-right:10px;
float:left;
position:absolute;
line-height: 100px;
padding: 0px;
margin: 0px;
}
also made some improvements to your animation:
function transition() {
$('.newsticker p').animate({"marginLeft":"400px","opacity":".0"}, 600, function() {
$('.newsticker p').remove();
$('.newsticker').append("<p style='margin-left:400px;opacity:0'>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam </p>");
$('.newsticker p').animate({"marginLeft":"0px","opacity":"1"}, 600);
});
}
setInterval(transition, 2000);
you have to start with this:
<div class="newsticker">
<p><p>
</div>
I have updated the fiddle, you can see the result here
This is the css applied on parent and child respectively
//parent
position:relative;
//child
position:absolute/relative;
top:50%;
height:x;
margin-top:-x/2; // half the height
There are two ways to vertically center align a block.
Take top position to 50%, and negative margin to half the height of block. This will force it to align vertically center. This is useful only when you know the size of the block.
Use the display technique. Apply 'display:table-cell' to the parent container and for the child container use 'vertical-align:middle' property. This will align your block vertically whatever the size may change to.