Prevent background scrolling when overlay appears - javascript

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 :-(.

Related

Javascript not working on ipad (tested on ios 13.7 and ios 14.0 - in chrome, safari and firefox) or iphone (tested on iphone SE ios 13.7)

I wonder if you can help me please? I am trying to get a a piece of javascript to work on ios devices. I have multiple images on a page in divs and on click I would like to slide across a panel of information (hidden to the left) and once that panel slides in, if that panel is clicked, I would like it to slide back out to the left.
Here is the code I have that works perfectly on all desktop browsers but doesn't do a thing on ios - I can't understand it and am uncertain how to test or check for problems on ios.
Here is a Fiddle: https://jsfiddle.net/adpLqbt2/31/
CSS
.box {
display: inline-block;
position: relative;
width: 500px;
height: 500px;
overflow: hidden;
}
.overlay-btn {
display: inline-block;
position: absolute;
cursor: pointer;
height: 500px;
width: 500px;
z-index: 10;
background: url('../img/icons/info.svg') 98% 98% no-repeat;
}
.overlay-content {
position: absolute;
top: 0;
left: -500px;
width: 500px;
margin: 0;
height: 500px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
transition: all 0.5s ease-in-out;
-webkit-transition: all 0.5s ease-in-out;
}
.over-show {
left: 0;
height: 500px;
transition: all 0.5s ease-out;
-webkit-transition: all 0.5s ease-out;
}
HTML
<div class="box">
<div class="overlay-btn"></div>
<div class="overlay-content"><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div>
<img src="https://via.placeholder.com/500" alt="rah" />
</div>
<div class="box">
<div class="overlay-btn"></div>
<div class="overlay-content"><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div>
<img src="https://via.placeholder.com/500" alt="yay" />
</div>
<div class="box">
<div class="overlay-btn"></div>
<div class="overlay-content"><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div>
<img src="https://via.placeholder.com/500" alt="see" />
</div>
JS
const triggers = document.getElementsByClassName('overlay-btn');
const triggerArray = Array.from(triggers).entries();
const panels = document.getElementsByClassName('overlay-content');
const infoButtons = document.getElementsByClassName('overlay-btn');
for (let [index, trigger] of triggerArray) {
let triggerIndex = index;
function togglePanel() {
panels[triggerIndex].classList.toggle('over-show');
infoButtons[triggerIndex].classList.toggle('js-fade');
}
trigger.addEventListener('click', togglePanel);
infoButtons[triggerIndex].addEventListener('click', togglePanel);
}

fading scrollbar when not scrolling

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/

How to make an HTML element to expand on hover and overlay similar neighbor elements in the same flex container without moving them?

There are N cards of the same class, each with a different amount of text inside. All cards are in the same flex container (row, wrap). In some cards part of the text is hidden because it exceeds the dimension limits for the class. I want such cards to expand and show the entire content on mouse hover. So far I can make it either to overlay the text only (as in the image) or to expand while moving the neighbors. Instead, such card should overlay above neighbor cards without moving them.
Ideally, the hovered card should expand both horizontally (if possible, symmetrically, otherwise to the left or to the right only), and vertically downwards.
Here is a very simple example created for this question. Each card contains a random length substring of lorem ipsum.
JavaScript, generates cards with random amount of text:
'use strict';
const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ac auctor augue mauris augue neque gravida in fermentum et. Accumsan in nisl nisi scelerisque. Sed pulvinar proin gravida hendrerit lectus. Tortor id aliquet lectus proin nibh nisl condimentum. Erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit.';
const items = Array(30).fill(0)
.map(() => Math.ceil(lorem.length * Math.random()))
.map(idx => lorem.substring(0, idx))
.map(txt => `<div class="lorem-card">${txt}</div>`)
.join('');
const html = `<div class="cards-container">${items}</div>`;
document.body.innerHTML += html;
html is a skeleton only because the content is generated by JavaScript
<html>
<head>
<meta charset="utf-8">
<title>Cards demo</title>
<link rel="stylesheet" type="text/css" href="./cards.css">
</head>
<body>
</body>
<script src="./gen.js"></script>
</html>
And here is current CSS:
.cards-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: flex-start;
align-content: space-between;
}
.lorem-card {
flex-grow: 1;
background-color: white;
border: 3px solid #008CBA;
padding: 15px;
margin: 15px;
max-width: 150px;
max-height: 200px;
overflow: hidden;
opacity: 1;
}
.lorem-card:hover {
flex-grow: 10;
height: auto;
width: auto;
overflow: visible;
border: 3px solid red;
z-index: 100;
}
https://jsfiddle.net/fpmuc5Lz/3/
So far, this is what I have concluded: it is impossible to not cause the other divs to move while overlaying an element with an initially not absolutely positioned element. As a result, I've decided to create a copy element that is absolutely positioned that has exactly the same width of the original element. This is what I changed in your JS:
'use strict';
const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ac auctor augue mauris augue neque gravida in fermentum et. Accumsan in nisl nisi scelerisque. Sed pulvinar proin gravida hendrerit lectus.';
const items = Array(30).fill(0)
.map(() => Math.ceil(lorem.length * Math.random()))
.map(idx => lorem.substring(0, idx))
.map(txt => `<div class="lorem-card"><div class="lorem-card--real">${txt}</div><div class="lorem-card--substitute">${txt}</div></div>`)
.join('');
const html = `<div class="cards-container">${items}</div>`;
document.body.innerHTML += html;
And your CSS:
.lorem-card {
position: relative;
flex-grow: 1;
max-width: 150px;
max-height: 200px;
margin: 15px;
overflow: hidden;
}
.lorem-card--real {
max-width: 150px;
max-height: 200px;
padding: 15px;
border: 3px solid #008CBA;
background: #FFF;
text-overflow: ellipsis;
}
.lorem-card--substitute {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 15px;
background: #FFF;
border: 3px solid #008CBA;
}
.lorem-card:hover {
overflow: visible;
}
.lorem-card:hover .lorem-card--real {
opacity: 0;
}
.lorem-card:hover .lorem-card--substitute{
height: auto;
overflow: visible;
border: 3px solid red;
z-index: 100;
}
The idea is to create a container for two Lorem cards. One that has a set width and height (200px and 150px at maximum) and one that can expand dynamically and overlay other elements without causing other divs to move (by using position: absolute). As can be seen, the effect is that the absolutely positioned element only expands vertically downwards. Furthermore, as height is set to auto, CSS transition does not work, which leads to the following suggestion.
My current idea, if you want it to expand both horizontally both ways and vertically downwards, is to compute the total width of the overall string (arranged in one horizontal line) and the height the largest character. Then, use some math to constraint the div's width and height ratio to 4:3 (while handling edge cases, e.g. specifying min-width and min-height) until you find an area that fits the whole text. This will allow you to have the exact width and height needed and as you can specify the exact width and height, you can also use CSS transition to smoothly expand the div.
Here is the new code: here
EDIT
After fiddling some more, here's the final code. In summary, what I added:
A dummy function that you can configure to decide how much the div should expand
Detection of whether an div needs to expand based on its TextNode's bounding rectangle
A smooth transition (the timeout logic has not been tested with edge cases)
Here's the full-working example:
'use strict';
const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ac auctor augue mauris augue neque gravida in fermentum et. Accumsan in nisl nisi scelerisque. Sed pulvinar proin gravida hendrerit lectus.';
const items = Array(30).fill(0)
.map(() => Math.ceil(lorem.length * Math.random()))
.map(idx => lorem.substring(0, idx))
.map(txt => `<div class="lorem-card"><div class="lorem-card--real">${txt}</div><div class="lorem-card--substitute">${txt}</div></div>`)
.join('');
const html = `<div class="cards-container">${items}</div>`;
document.body.innerHTML += html;
function getTextWidthAndHeight() {
return [210, 280]
}
let cards = document.querySelectorAll('.lorem-card')
let resetCardOverflow
for (let card of cards) {
card.addEventListener('mouseover', e => {
clearTimeout(resetCardOverflow)
card.style.overflow = 'visible'
// Configure "getTextWidthAndHeight" to fit the new rectangle size needs
let size = getTextWidthAndHeight()
let targetReal = card.querySelector('.lorem-card--real')
let targetSubstitute = card.querySelector('.lorem-card--substitute')
let textNode
for (let child of targetReal.childNodes) {
if (child.nodeName == '#text') {
textNode = child
break
}
}
// Get "height" of textNode's bounding box
let textHeight = 0;
let range = document.createRange();
range.selectNodeContents(textNode);
if (range.getBoundingClientRect) {
var rect = range.getBoundingClientRect();
if (rect) {
textHeight = rect.bottom - rect.top;
}
}
// If text exceeds box height (padding considered)
if (textHeight > 200 - 30) {
targetSubstitute.style.width = `${size[0]}px`
targetSubstitute.style.height = `${size[1]}px`
targetSubstitute.style.transform = `translateX(-30px)`
}
})
card.addEventListener('mouseleave', e => {
let targetSubstitute = card.querySelector('.lorem-card--substitute')
targetSubstitute.style.width = ''
targetSubstitute.style.height = ''
targetSubstitute.style.transform = ''
resetCardOverflow = setTimeout(() => {
card.style.overflow = ''
}, 200)
})
}
* {
box-sizing: border-box;
}
.cards-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: flex-start;
align-content: space-between;
}
.lorem-card {
position: relative;
flex-grow: 1;
max-width: 150px;
max-height: 200px;
margin: 15px;
overflow: hidden;
}
.lorem-card--real {
max-width: 150px;
max-height: 200px;
padding: 15px;
border: 3px solid #008CBA;
background: #FFF;
overflow: hidden;
}
.lorem-card--substitute {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 15px;
background: #FFF;
border: 3px solid #008CBA;
transition: all .2s ease;
overflow: hidden;
}
.lorem-card:hover .lorem-card--real {
opacity: 0;
}
.lorem-card:hover .lorem-card--substitute{
border: 3px solid red;
z-index: 1000;
}
<html>
<head>
<meta charset="utf-8">
<title>Cards demo</title>
<link rel="stylesheet" type="text/css" href="./cards.css">
</head>
<body>
</body>
<script src="./gen.js"></script>
</html>
In case you need the JS fiddle code: here

Slick carousel - force slides to have the same height

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);
});
}

Flexbox text cutting off/not wrapping

I'm using flexbox to display a blockquote and author/avatar horizontal to each other. This is within a slideshow (flexslider) but that doesn't seem to be the reason for the problem.
This works ok until we hit IE10. It appears to work fine in Chrome, Firefox, Opera, Safari, Edge and IE11.
The problem I'm having is the text is cut off at the end of the quote. If you don't see this at first you may have to resize your viewport. This may be caused by the padding I have at each side of the text to allow space for the custom open/close quotation marks.
Another issue in IE10 is that when the text is long (see "James Hetfield Longname" on the first quote) it doesn't wrap. This could be related to my other issue as I guess the text isn't wrapping correctly then either.
Here's some links to an example. I've include a CodePen and also a stripped back version of my HTML template.
CodePen: http://codepen.io/moy/pen/XdLELV
Template: http://moymadethis.com/flex/quote.html
Really hope someone can help with this!
Here's the code, as it's making my add something (though I don't think this wall off CSS/HTML is particually helpful myself)!
EDIT: I should add that I use Autoprefixer to popular the extra flex prefixes.
HTML:
<div class="flexslider">
<ul class="slides">
<li>
<blockquote class="feature-quote">
<p class="feature-quote__text">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.</p>
<footer class="feature-quote__cite">
<img src="img/temp/avatars/avatar-james.jpg" class="feature-quote__avatar" />
<p><strong class="name">James Hetfield Hetfield</strong> Damage Inc.</p>
</footer>
</blockquote>
</li>
<li>
<blockquote class="feature-quote">
<p class="feature-quote__text">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.</p>
<footer class="feature-quote__cite">
<img src="img/temp/avatars/avatar-james.jpg" class="feature-quote__avatar" />
<p><strong class="name">James Hetfield</strong> Damage Inc.</p>
</footer>
</blockquote>
</li>
<li>
<blockquote class="feature-quote">
<p class="feature-quote__text">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.</p>
<footer class="feature-quote__cite">
<img src="img/temp/avatars/avatar-james.jpg" class="feature-quote__avatar" />
<p><strong class="name">James Hetfield</strong> Damage Inc.</p>
</footer>
</blockquote>
</li>
</ul>
</div>
CSS:
/* Base blockquote styles */
blockquote {
margin-bottom: $baseline*2;
overflow: hidden; // Fixes bug when inside flexslider when open/close quote-marks duplicate.
padding: $baseline $baseline 0 0;
p {
margin-bottom: $baseline/2;
}
> p {
color: $blue-light;
#include font-size(25);
line-height: $baselineheight/1.25;
font-weight: 300;
padding-left: 30px;
-webkit-font-smoothing: antialiased;
&:before,
&:after {
background: url(../img/content/quote-open.png) no-repeat 0 0;
content: "";
display: inline-block;
height: 24px;
margin: 0 10px 0 -30px;
position: relative;
top: -5px;
width: 21px;
}
&:after {
background: url(../img/content/quote-close.png) no-repeat 0 0;
margin: 5px 0 0 5px;
position: absolute;
top: auto;
}
}
footer {
padding-left: 30px;
}
.name {
color: $blue;
display: block;
text-transform: uppercase;
}
}
/* Feature (avatar) quotes */
.feature-quote {
margin-bottom: $baseline;
padding-top: 5px;
}
.feature-quote footer p {
display: inline-block;
margin-bottom: 0;
vertical-align: middle;
}
.feature-quote__cite {
margin-top: $baseline;
}
.feature-quote__avatar {
border: 5px solid $blue-lighter;
border-radius: 100%;
display: inline-block;
height: 60px;
margin-right: $baseline/2;
width: 60px;
}
/* Above 768px (Feature quote side-by-side */
#media only screen and (min-width: 768px) {
blockquote {
margin: 0 25px $baseline*2;
}
.feature-quote {
display: flex;
}
.feature-quote__text {
order: 2;
width: 66.66666%;
}
.feature-quote__cite {
display: flex;
align-items: center;
justify-content: center;
order: 1;
margin-top: 0;
padding-right: 30px;
width: 33.33333%;
}
.feature-quote__avatar {
height: 80px;
width: 80px;
}
.no-flexbox {
.feature-quote {
margin: 0 auto $baseline;
max-width: 800px;
}
.feature-quote__text,
.feature-quote__cite {
text-align: center;
width: 100%;
}
.feature-quote__cite {
p {
text-align: left;
}
}
}
}
Expanding upon Pete's comment about IE10 not properly supporting flexbox.
http://caniuse.com/#search=flex
Regarding IE10:
Only supports the 2012 syntax
Need the -ms- prefix
This answer actually has lots of information about flex in IE10: https://stackoverflow.com/a/21306343/2117156
Note I'm using autoprefixer so all -ms- prefixes are generated automatically. I will just note the prefix-less declarations here.
Adding the following line onto the paragraph did the trick flex: 0 1 auto;
I also had an issue where the text wouldn't wrap in the .feature-quote__cite container. I tried adding the above which didn't work. To fix this I had to add flex: 0 1 auto; directly onto the paragraph within rather than on the parent container .feature-quote__cite. Not ideal but it looks like it's done the trick.
As an aside, in IE11 the avatar image would get squashed when there wasn't enough horizontal space. I found adding flex-shrink: 0; to the image fixed this.

Categories