Addding elements to centered flexbox from left to right - html

I have a flex box to center 3 divs in the screen. all the three are wrapped around a flex container that has justify-content: center which centers them in the screen. However, since I am using animation and each div is being added one after another, the first div gets added at the center instead of the beginning, the second gets added such that both of the first and second are centered, and when the third is added, all of them are aligned such that the second div is exactly in the middle of the screen. I want to change that such that each div gets added in-place from left to right while all are centered in the screen.
React code:
function foo() {
const [items, set] = React.useState([]);
const transitions = useTransition(items, (item) => item.key, {
from: { transform: "translate3d(0,-40px,0)" },
enter: { transform: "translate3d(0,0px,0)" },
leave: { transform: "translate3d(0,-40px,0)" },
});
React.useEffect(() => {
for (let i = 0; i < rows.length; i++) {
setTimeout(() => {
set((items) => {
const newItems = [...items];
newItems.push({ key: i, code: rows[i] });
return newItems;
});
}, 1000 * i);
}
}, []);
return (
<center>
<div class="flex-container">
{transitions.map(({ item, props, key }) => {
return (
<animated.div key={key} style={props} class="flex-item">
{item.code}
</animated.div>
);
})}
</div>
</center>
);
}
Styling:
.flex-container {
height: 50vh;
display: flex;
justify-content: center;
img{
width: 20%;
text-align: center;
}
}
.flex-item {
flex-basis: 14%;
align-self: center;
margin: 12px;
h4{
font-family: Lato;
font-weight:700;
color: white;
}
p{
color: white;
font-family: Lato;
}
}
Changing center to flex-start shifts everything to the beginning:
Adding fixed width doesn't work as well:

To solve your problem, simply align the cards the way you want them to look after they move, then apply the following CSS:
.card {
position: relative;
top: -100px;
}
If -100px is not enough, try -200px. The goal is that the cards will be off-screen at the top. Upon whatever action you desire, whether it be clicking a button like in my snippet below, or perhaps on page load, change the "top" property to 0, and it will place the card where it belongs in the normal flow of the page.
I additionally use the setTimeout function to delay each subsequent card, and the transition property to animate the movement smoothly since it seems like that is your desired goal.
const left = document.getElementById("left");
const center = document.getElementById("center");
const right = document.getElementById("right");
function foo() {
left.style.top = "0";
setTimeout(function() {
center.style.top = "0";
setTimeout(function() {
right.style.top = "0";
}, 500)
}, 500)
}
#container {
display: flex;
justify-content: center;
}
#container>* {
width: 100px;
height: 50px;
background: red;
margin: 0 0.5em;
position: relative;
top: -100px;
transition: top 1s ease-in-out;
}
button {
margin: auto;
}
<div id="container">
<div id="left"></div>
<div id="center"></div>
<div id="right"></div>
</div>
<button onclick="foo()" id="click-me">Click me!</button>

Related

Is there any way in CSS to make other objects move when another object is scaled?

I have a row of elements, and they have a hover animation to make them scale up. Is it possible to make other images next to them change position on scale to prevent the overlap?
body {
background-color:#1a1a1a;
}
img{
max-width: 15%;
transition-duration: 0.5s;
transform-origin: center;
border-radius: 25px;
overflow: hidden;
margin: 50px;
}
img:hover{
cursor: pointer;
transform: scale(110%);
}
<img src="https://www.tazzadesign.com/wp-content/uploads/sites/65/2013/11/dummy-image-square.jpg">
<img src="https://www.tazzadesign.com/wp-content/uploads/sites/65/2013/11/dummy-image-square.jpg">
<img src="https://www.tazzadesign.com/wp-content/uploads/sites/65/2013/11/dummy-image-square.jpg">
and example of the effect I am looking for would be something that looks like this:
This will scale images up and down, dependent on classes. I've amended your css slightly for display purposes and add the JS code (left comments as clear as possible).
// define function to return all siblings of hovered element
function getAllSiblings(element, parent) {
const children = [...parent.children];
return children.filter((child) => child !== element);
}
// grab all img elements
const imgs = document.querySelectorAll('img');
imgs.forEach((i) => {
// define siblings using function above
const siblings = getAllSiblings(i, document.getElementById('parent'));
// *hover in*
i.addEventListener('mouseover', function () {
// add an active class when hovered (amended your css)
this.classList.add('active');
// add small class to all sibling elements
siblings.forEach((sibling) => {
sibling.classList.add('small');
});
});
// *hover out*
i.addEventListener('mouseleave', function () {
// remove active class and small classes so hovered element reverts to normal
this.classList.remove('active');
this.classList.contains('small') && this.classList.remove('small');
// remove class small on all siblings so that everything is reverted to initial display
siblings.forEach((sibling) => {
sibling.classList.remove('small');
});
});
});
body {
background-color: #1a1a1a;
/* added for display purposes */
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* added for getting parent element */
#parent {
margin: auto;
display: flex;
justify-content: center;
align-items: center;
gap: 2rem;
width: 100%;
}
img {
max-width: 15%;
transition-duration: 0.5s;
transform-origin: top;
transform-origin: left;
border-radius: 25px;
}
/* added for changing hover states */
img.active {
max-width: 17%;
cursor: pointer;
transform: scale(120%);
transform-origin: center;
}
img.small {
max-width: 17%;
transform: scale(80%);
transform-origin: center;
}
<div id="parent">
<img
src="https://www.tazzadesign.com/wp-content/uploads/sites/65/2013/11/dummy-image-square.jpg"
/>
<img
src="https://www.tazzadesign.com/wp-content/uploads/sites/65/2013/11/dummy-image-square.jpg"
/>
<img
src="https://www.tazzadesign.com/wp-content/uploads/sites/65/2013/11/dummy-image-square.jpg"
/>
</div>
Check this out (after hover, the shape is changing width & height):
const divs = document.querySelectorAll('div');
const reset = () => {
divs.forEach(div => div.classList.remove('active'))
}
divs.forEach(div => div.addEventListener('mouseover', () => {
reset();
div.classList.toggle('active');
}));
divs.forEach(div => div.addEventListener('mouseout', () => {
reset();
}))
section {
height: 150px;
width: 600px;
display: flex;
justify-content: space-between;
align-items: center;
border: 2px solid red;
}
div {
flex: 1;
height: 100px;
border: 2px solid black;
transition: flex .2s, transform .2s;
margin: 1em;
}
div.active {
transform: scaleY(1.5);
flex: 2;
}
<section>
<div></div>
<div></div>
<div></div>
<div></div>
</section>

OnMouseOver change the content of a (pop-up) window

I'm a visual artist with not that many coding skills. I know some HTML and some CSS but that's it. I like to create a webpage that does the following:
On the left, there is an image with lines. When hovering over a line the window on the right shows an image, movie, or plays a sound. Hovering over the next line triggers another image, movie, or sound.
Anyone can point me in the correct direction? I made a gif to show how it should work...
Simple solution:
Select HTML elements which we want to hover over (left, middle, right), and HTML elements which contain our images/videos/audio etc. (img1, sound, img2)
For every element you want to hover over, you need to add event listener (addEventListener), so you can manipulate your HTML/CSS code with JavaScript.
2.2 Inside each event listener you add or remove class: none, which has CSS value of display: none (this means element won't be shown), depending on what your goal is.
To make images disappear when we don't hover our cursor over the element, we need to again add event listener to elements which already have on mouseover event listener. In this case we use mouseover or blur. When cursor isn't on the element, JavaScript will automatically add none class to it.
const left = document.querySelector('.left-line');
const middle = document.querySelector('.middle-line');
const right = document.querySelector('.right-line');
const img1 = document.querySelector('.image-1');
const sound = document.querySelector('.sound');
const img2 = document.querySelector('.image-2');
left.addEventListener('mouseover', () => {
img1.classList.remove('none');
img2.classList.add('none');
sound.classList.add('none');
});
middle.addEventListener('mouseover', () => {
img1.classList.add('none');
img2.classList.remove('none');
sound.classList.add('none');
});
right.addEventListener('mouseover', () => {
img1.classList.add('none');
img2.classList.add('none');
sound.classList.remove('none');
});
left.addEventListener('mouseout',() => addNoneClass());
middle.addEventListener('mouseout', () => addNoneClass());
right.addEventListener('mouseout', () => addNoneClass());
function addNoneClass() {
img1.classList.add('none');
img2.classList.add('none');
sound.classList.add('none');
}
* {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
}
main {
display: flex;
width: 100%;
height: 100%;
}
section.left {
width: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.line-container {
width: 150px;
height: 150px;
display: flex;
align-items: center;
border: 2px solid black;
}
.left-line, .middle-line, .right-line {
width: 50px;
height: 90%;
margin: 0 10px;
}
.left-line { background-color: green; }
.middle-line { background-color: red; }
.right-line { background-color: blue; }
section.right {
width: 50%;
display:flex;
align-items: center;
justify-content: center;
}
.box {
width: 200px;
height: 200px;
border: 2px solid black;
}
img {
width: 200px;
height: 200px;
}
.none {
display: none;
}
<main>
<section class="left">
<div class="line-container">
<div class="left-line">
</div>
<div class="middle-line">
</div>
<div class="right-line">
</div>
</div>
</section>
<section class="right">
<div class="box">
<div class="image-1 none">
<img src="https://play-lh.googleusercontent.com/aFWiT2lTa9CYBpyPjfgfNHd0r5puwKRGj2rHpdPTNrz2N9LXgN_MbLjePd1OTc0E8Rl1" alt="image-1">
</div>
<div class="sound none">
<img src="https://sm.pcmag.com/pcmag_uk/review/g/google-pho/google-photos_z68u.jpg" alt="sound">
</div>
<div class="image-2 none">
<img src="https://cdn.vox-cdn.com/thumbor/I2PsqRLIaCB1iYUuSptrrR5M8oQ=/0x0:2040x1360/1200x800/filters:focal(857x517:1183x843)/cdn.vox-cdn.com/uploads/chorus_image/image/68829483/acastro_210104_1777_google_0001.0.jpg" alt="image-2">
</div>
</div>
</section>
</main>
You can do this by the following code example.
HTML:
<div class="lines">
<span id='line-1'>|</span>
<span id='line-2'>|</span>
<span id='line-3'>|</span>
</div>
<div id='output'></div>
JS
const line1 = document.getElementById('line-1')
const line2 = document.getElementById('line-2')
const line3 = document.getElementById('line-3')
const output = document.getElementById('output')
line1.addEventListener('mouseover', () => {
output.innerHTML = 'Content One'
})
line2.addEventListener('mouseover', () => {
output.innerHTML = 'Content Two'
})
line3.addEventListener('mouseover', () => {
output.innerHTML = 'Content Three'
})

Having issues with vertical alignment (Flexbox) and center alignment

I'm using javascript to display dynamic text and image, but having trouble with formatting.
I'm using display: flex to put text and image next to each other, but am having trouble horizontally aligning them. Right now, it looks like:
But I'd like to horizontally align them so that it becomes:
I've tried the following, but this didn't work
#conA #container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
Update:
Implementing the following code but NOT setting the height of #heroText and #images the same
#conA #container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
does center text and images horizontally when both are present. However, once the images disappear, the text jumps. Here's how it's behaving https://imgur.com/a/7yYl8zO I'd like the text to not move when images disappear
Once I set the heights of #heroText and #images the same, it then turns to this:
I'm also looking to center the whole text+image in the parent div (#conA, which takes up full screen 100vh). I tried the following:
#conA {
position: relative;
}
#conA #container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
But it looks odd b/c text keeps moving/jumping depending on whether there's an image displayed.
So when there's no image, it looks like:
When when images appear, text moves to left so that the whole thing becomes centered:
How can I fix the position of text here? I'd like the whole thing is centered when there's an image. So when there's no image:
When there's an image:
html
<section id="conA">
<div id="container">
<div id="heroText">
<div id="text-fixed">I'm a fixed text</div>
<div id="text"></div>
</div>
<div id="images"></div>
</div>
</section>
css
#conA {
height: 100vh;
position: relative;
}
#conA #container {
margin: 0;
}
#conA #text {
display: initial;
border-right: 3px solid #56525E;
}
#heroText {
line-height: 1.7;
font-size: 30px;
width: 800px;
}
#conA #container {
margin: 0;
display: flex;
}
#heroText {
height: 400px;
}
#conA #images {
height: 400px;
}
#conA img
{
display:none;
height: 400px;
}
#conA img.invisible
{
visibility: hidden;
}
#conA img.show
{
display:inline;
}
#conA img.anim1
{
animation-duration: 2000ms;
}
#conA img.anim2
{
animation-duration: 2000ms;
}
#conA img.anim3
{
animation-duration: 2000ms;
}
.fadeIn
{
animation-name: fadeIn;
}
#keyframes fadeIn
{
0% {opacity: 0;}
100% {opacity: 1;}
}
.fadeOut
{
animation-name: fadeOut;
}
javascript code for dynamic display of text and image
// List of sentences
var _CONTENT = [ "I'm the first sentence.", "I'm the second sentence."
, "I'm the third sentence.", "I'm the fourth sentence." ];
var IMAGE_URLS = ['img/image1.png', 'img/image2.jpg', 'img/image3.png', 'img/image1.png','img/image2.png','img/image3.png', 'img/image4.png','img/image5.png'];
var IMAGES = jQuery.map(IMAGE_URLS, function (url, index){
var img = document.createElement('img');
img.setAttribute('src', url);
img.classList.add('anim'+((index%2)+1));
img.classList.add('fadeOut');
document.getElementById('images').appendChild(img);
return img;
});
// Current sentence being processed
var _PART = 0;
// Character number of the current sentence being processed
var _PART_INDEX = 0;
// Holds the handle returned from setInterval
var _INTERVAL_VAL;
// Element that holds the text
var _ELEMENT = document.querySelector("#text");
// Implements typing effect
function Type() {
var text = _CONTENT[_PART].substring(0, _PART_INDEX + 1);
_ELEMENT.innerHTML = text;
_PART_INDEX++;
// If full sentence has been displayed then start to delete the sentence after some time
if(text === _CONTENT[_PART]) {
var imgIndexBase = _PART*2;
IMAGES[imgIndexBase].classList.remove('fadeOut');
IMAGES[imgIndexBase+1].classList.remove('fadeOut');
setTimeout(function () { IMAGES[imgIndexBase].classList.add('fadeIn'); }, 0);
setTimeout(function () { IMAGES[imgIndexBase].classList.add('show'); }, 0);
setTimeout(function () { IMAGES[imgIndexBase].classList.add('fadeOut'); }, 2000);
setTimeout(function () { IMAGES[imgIndexBase].classList.remove('fadeOut'); }, 3000);
setTimeout(function () { IMAGES[imgIndexBase].classList.remove('show'); }, 3000);
setTimeout(function () { IMAGES[imgIndexBase + 1].classList.add('fadeIn'); }, 0);
setTimeout(function () { IMAGES[imgIndexBase + 1].classList.add('show'); }, 0);
setTimeout(function () { IMAGES[imgIndexBase + 1].classList.add('fadeOut'); }, 2000);
setTimeout(function () { IMAGES[imgIndexBase + 1].classList.remove('fadeOut'); }, 3000);
setTimeout(function () { IMAGES[imgIndexBase + 1].classList.remove('show'); }, 3000);
clearInterval(_INTERVAL_VAL);
setTimeout(function() {
_INTERVAL_VAL = setInterval(Delete, 50);
}, 4000);
}
}
// Implements deleting effect
function Delete() {
var text = _CONTENT[_PART].substring(0, _PART_INDEX - 1);
_ELEMENT.innerHTML = text;
_PART_INDEX--;
// If sentence has been deleted then start to display the next sentence
if(text === '') {
clearInterval(_INTERVAL_VAL);
// If last sentence then display the first one, else move to the next
if(_PART == (_CONTENT.length - 1))
_PART = 0;
else
_PART++;
_PART_INDEX = 0;
// Start to display the next sentence after some time
setTimeout(function() {
_INTERVAL_VAL = setInterval(Type, 100);
}, 500);
}
}
// Start the typing effect on load
_INTERVAL_VAL = setInterval(Type, 100);
See example below using flex CSS. First section with image, second section with no images in .images div.
There is a lot to explain with flex, but it is super powerful when it comes to dynamic layout. Hope this gets you on the right track.
Also you need to relax on your id attribute usage, valid html only allow single usage of an id attribute value. Use class attribute for multiple instances, and id for single usage instances.
id attribute value should only ever be used once, never multiple times.
BODY {
padding: 1rem;
margin: 0;
}
SECTION .container {
border: 1px black solid;
padding: 2rem;
display: flex;
align-items: center;
flex-wrap: wrap;
flex-direction: initial;
min-height: 100px;
justify-content: center;
margin-bottom: 1rem;
}
SECTION .container .hero-text {
width: 50%;
background: cyan;
}
SECTION .container .images {
width: 50%;
background: red;
}
SECTION .container .images IMG {
display: block;
width: 100%;
}
<section>
<div class="container">
<div class="hero-text">
<div class="text-fixed">I'm a fixed text</div>
<div class="text">And I am loving life :)</div>
</div>
<div class="images">
<img src="https://i.imgur.com/q5Y5RCH.png" alt="" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="hero-text">
<div class="text-fixed">I'm a fixed text</div>
<div class="text">And I am loving life :)</div>
</div>
<div class="images">
<!-- no images -->
</div>
</div>
</section>

Custom onpage html based on selected slider image

Apologies if I incorrectly use some terminology here, or don't know the terms to properly describe this but...
Easy Part- I would like to create a Wheel style slider, displaying three pictures, with the main "selected" one being forefront and the other two sitting scaled down, behind them but quickly and easily clickable and viewable.
Hard Part- I would like which ever picture is set in the forefront main portion of the slider wheel to have information displayed, page width, regarding that specific toggled picture and that picture only. Then when you toggle to a different slide/picture ONLY information regarding that would then be placed below the slider/toggle wheel.
I would think my starting point would be to grab some code for the toggle/slider picture wheel. Then somehow create some sort of event trigger type coding for whichever picture is highlighted, coupling that with some sort of html hide/show coding.
I attached some bad sketches to help me visual depict what I am saying.
Any insight is welcome, even if it is some keywords to help me narrow down my google searching and find some resources. Slide & Page Layout Sketch
Thanks & Cheers
Sounds like you're going to need some CSS and JS to make this work.
First, you're going to need your HTML layout. I have a wrapper for the entire carousel (.container). I have the left and right arrows as well as a second wrapper for the images.
For the text below the carousel, I have a second element (.content) which holds three elements, each correlating to the images. The text is only shown when .shown is applied to the element.
<div class="container">
<div class="left"><</div>
<div>
<div class="img img-left"></div>
<div class="img img-center"></div>
<div class="img img-right"></div>
</div>
<div class="right">></div>
</div>
<div class="content">
<div class="text">
Amazing Sunset
</div>
<div class="text shown">
Fall Leaves
</div>
<div class="text">
Misty Sunlight
</div>
</div>
For CSS I choose to make the .container position: relative so that I could use position: absolute on the children. I have 3 classes for the images. img-left, img-right and img-center. These can be animated. The arrows are simply centered vertically
.container {
position: relative;
height: 85vh;
}
.img {
position: absolute;
z-index: 2;
height: 75vh;
width: 121vh;
max-height: 300px;
max-width: 511px;
top: 0; bottom: 0;
left: 0; right: 0;
margin: auto;
transition: transform 0.3s, z-index 0s linear 0.15s;
}
.img:nth-of-type(1) {
background: url('https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__340.jpg') center/contain no-repeat;
}
.img:nth-of-type(2) {
background: url('https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823__340.jpg') center/contain no-repeat;
}
.img:nth-of-type(3) {
background: url('https://cdn.pixabay.com/photo/2015/09/09/16/05/forest-931706__340.jpg') center/contain no-repeat;
}
.img-center {
z-index: 5;
}
.img-right {
transform: translateX(200px) scale(0.7);
}
.img-left {
transform: translateX(-200px) scale(0.7);
}
.left, .right {
position: absolute;
z-index: 7;
top: 50%;
font-size: 48px;
font-family: monospace;
transform: translateY(-50%);
user-select: none;
}
.left {
left: 32px;
}
.right {
right: 32px;
}
.content {
height: 15vh;
}
.text {
display: none;
text-align: center;
font-size: 32px;
}
.text.shown {
display: block;
}
JavaScript is where things start to get more interesting. I created a function called nextImage() which takes a boolean representing the direction to switch. First, it gets the currently centered image and then based on that image gets the next and previous element siblings. In the event that the centered image happens to be the first or last element, either next or pre will be undefined. That is handled next. Once that is done the CSS classes are reassigned based on the direction.
function nextImage(forward) {
let currentCentered = document.querySelector('.img-center'),
next = currentCentered.nextElementSibling,
pre = currentCentered.previousElementSibling;
//pre and next may not be elements if currentCentered is the frist or last element.
if (!next) { //Centered Element is the frist
next = pre.previousElementSibling;
} else if (!pre) { //Centered Element is the last
pre = next.nextElementSibling;
}
if (forward) {
//Move the previously centered image to the right
currentCentered.classList.remove('img-center');
currentCentered.classList.add('img-right');
//Move the previously left image to the center
pre.classList.remove('img-left');
pre.classList.add('img-center');
//Move the previously right image to the left
next.classList.remove('img-right');
next.classList.add('img-left');
} else {
//Move the previously centered image to the left
currentCentered.classList.remove('img-center');
currentCentered.classList.add('img-left');
//Move the previously left image to the right
pre.classList.remove('img-left');
pre.classList.add('img-right');
//Move the previously right image to the center
next.classList.remove('img-right');
next.classList.add('img-center');
}
//Update the text
let currentText = document.querySelector('.text.shown'),
newText;
if (forward) {
//Get the previous element;
newText = currentText.previousElementSibling;
//If it doesn't exist get the last element
if (!newText) {
newText = currentText.nextElementSibling.nextElementSibling;
}
} else {
//Get the next element;
newText = currentText.nextElementSibling;
//If it doesn't exist get the frist element
if (!newText) {
newText = currentText.previousElementSibling.previousElementSibling;
}
}
//Apply class change
currentText.classList.remove('shown');
newText.classList.add('shown');
}
Adding onclick="nextImage(false)" to the left arrow and onclick="nextImage(true)" to the right arrow now lets you navigate with them.
Since you said you'd like the pictures so be clickable I've added a second function which lets you scroll to the given image. It gets the next and previous elements and makes sure they are actual elements. Then it called the nextImage() function based on if next or pre is the centered image.
function switchImage(imgEle) {
let next = imgEle.nextElementSibling,
pre = imgEle.previousElementSibling;
//Make sure they are actually elements
if (!next) {
next = pre.previousElementSibling;
}
if (!pre) {
pre = next.nextElementSibling;
}
if (next.classList.contains('img-center')) {
nextImage(true);
} else if (pre.classList.contains('img-center')) {
nextImage(false);
}
}
All you have to do now is add onclick="switchImage(this)" to each of your image elements.
Adding this all together you should get something like this snippet below
function nextImage(forward) {
let currentCentered = document.querySelector('.img-center'),
next = currentCentered.nextElementSibling,
pre = currentCentered.previousElementSibling;
//pre and next may not be elements if currentCentered is the frist or last element.
if (!next) { //Centered Element is the frist
next = pre.previousElementSibling;
} else if (!pre) { //Centered Element is the last
pre = next.nextElementSibling;
}
if (forward) {
//Move the previously centered image to the right
currentCentered.classList.remove('img-center');
currentCentered.classList.add('img-right');
//Move the previously left image to the center
pre.classList.remove('img-left');
pre.classList.add('img-center');
//Move the previously right image to the left
next.classList.remove('img-right');
next.classList.add('img-left');
} else {
//Move the previously centered image to the left
currentCentered.classList.remove('img-center');
currentCentered.classList.add('img-left');
//Move the previously left image to the right
pre.classList.remove('img-left');
pre.classList.add('img-right');
//Move the previously right image to the center
next.classList.remove('img-right');
next.classList.add('img-center');
}
//Update the text
let currentText = document.querySelector('.text.shown'),
newText;
if (forward) {
//Get the previous element;
newText = currentText.previousElementSibling;
//If it doesn't exist get the last element
if (!newText) {
newText = currentText.nextElementSibling.nextElementSibling;
}
} else {
//Get the next element;
newText = currentText.nextElementSibling;
//If it doesn't exist get the frist element
if (!newText) {
newText = currentText.previousElementSibling.previousElementSibling;
}
}
//Apply class change
currentText.classList.remove('shown');
newText.classList.add('shown');
}
function switchImage(imgEle) {
let next = imgEle.nextElementSibling,
pre = imgEle.previousElementSibling;
//Make sure they are actually elements
if (!next) {
next = pre.previousElementSibling;
}
if (!pre) {
pre = next.nextElementSibling;
}
if (next.classList.contains('img-center')) {
nextImage(true);
} else if (pre.classList.contains('img-center')) {
nextImage(false);
}
}
.container {
position: relative;
height: 85vh;
}
.img {
position: absolute;
z-index: 2;
height: 75vh;
width: 121vh;
max-height: 300px;
max-width: 511px;
top: 0; bottom: 0;
left: 0; right: 0;
margin: auto;
transition: transform 0.3s, z-index 0s linear 0.15s;
}
.img:nth-of-type(1) {
background: url('https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__340.jpg') center/contain no-repeat;
}
.img:nth-of-type(2) {
background: url('https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823__340.jpg') center/contain no-repeat;
}
.img:nth-of-type(3) {
background: url('https://cdn.pixabay.com/photo/2015/09/09/16/05/forest-931706__340.jpg') center/contain no-repeat;
}
.img-center {
z-index: 5;
}
.img-right {
transform: translateX(200px) scale(0.7);
}
.img-left {
transform: translateX(-200px) scale(0.7);
}
.left, .right {
position: absolute;
z-index: 7;
top: 50%;
font-size: 48px;
font-family: monospace;
transform: translateY(-50%);
user-select: none;
}
.left {
left: 32px;
}
.right {
right: 32px;
}
.content {
height: 15vh;
}
.text {
display: none;
text-align: center;
font-size: 32px;
}
.text.shown {
display: block;
}
<div class="container">
<div class="left" onclick="nextImage(false)"><</div>
<div>
<div class="img img-left" onclick="switchImage(this)"></div>
<div class="img img-center" onclick="switchImage(this)"></div>
<div class="img img-right" onclick="switchImage(this)"></div>
</div>
<div class="right" onclick="nextImage(true)">></div>
</div>
<div class="content">
<div class="text">
Amazing Sunset
</div>
<div class="text shown">
Fall Leaves
</div>
<div class="text">
Misty Sunlight
</div>
</div>

Issues with resizing divs that are siblings and flex box [duplicate]

Here is an example chat app ->
The idea here is to have the .messages-container take up as much of the screen as it can. Within .messages-container, .scroll holds the list of messages, and in case there are more messages then the size of the screen, scrolls.
Now, consider this case:
The user scrolls to the bottom of the conversation
The .text-input, dynamically gets bigger
Now, instead of the user staying scrolled to the bottom of the conversation, the text-input increases, and they no longer see the bottom.
One way to fix it, if we are using react, calculate the height of text-input, and if anything changes, let .messages-container know
componentDidUpdate() {
window.setTimeout(_ => {
const newHeight = this.calcHeight();
if (newHeight !== this._oldHeight) {
this.props.onResize();
}
this._oldHeight = newHeight;
});
}
But, this causes visible performance issues, and it's sad to be passing messages around like this.
Is there a better way? Could I use css in such a way, to express that when .text-input-increases, I want to essentially shift up all of .messages-container
2:nd revision of this answer
Your friend here is flex-direction: column-reverse; which does all you ask while align the messages at the bottom of the message container, just like for example Skype and many other chat apps do.
.chat-window{
display:flex;
flex-direction:column;
height:100%;
}
.chat-messages{
flex: 1;
height:100%;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }
The downside with flex-direction: column-reverse; is a bug in IE/Edge/Firefox, where the scrollbar doesn't show, which your can read more about here: Flexbox column-reverse and overflow in Firefox/IE
The upside is you have ~ 90% browser support on mobile/tablets and ~ 65% for desktop, and counting as the bug gets fixed, ...and there is a workaround.
// scroll to bottom
function updateScroll(el){
el.scrollTop = el.scrollHeight;
}
// only shift-up if at bottom
function scrollAtBottom(el){
return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
In the below code snippet I've added the 2 functions from above, to make IE/Edge/Firefox behave in the same way flex-direction: column-reverse; does.
function addContent () {
var msgdiv = document.getElementById('messages');
var msgtxt = document.getElementById('inputs');
var atbottom = scrollAtBottom(msgdiv);
if (msgtxt.value.length > 0) {
msgdiv.innerHTML += msgtxt.value + '<br/>';
msgtxt.value = "";
} else {
msgdiv.innerHTML += 'Long long content ' + (tempCounter++) + '!<br/>';
}
/* if at bottom and is IE/Edge/Firefox */
if (atbottom && (!isWebkit || isEdge)) {
updateScroll(msgdiv);
}
}
function resizeInput () {
var msgdiv = document.getElementById('messages');
var msgtxt = document.getElementById('inputs');
var atbottom = scrollAtBottom(msgdiv);
if (msgtxt.style.height == '120px') {
msgtxt.style.height = 'auto';
} else {
msgtxt.style.height = '120px';
}
/* if at bottom and is IE/Edge/Firefox */
if (atbottom && (!isWebkit || isEdge)) {
updateScroll(msgdiv);
}
}
/* fix for IE/Edge/Firefox */
var isWebkit = ('WebkitAppearance' in document.documentElement.style);
var isEdge = ('-ms-accelerator' in document.documentElement.style);
var tempCounter = 6;
function updateScroll(el){
el.scrollTop = el.scrollHeight;
}
function scrollAtBottom(el){
return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
html, body { height:100%; margin:0; padding:0; }
.chat-window{
display:flex;
flex-direction:column;
height:100%;
}
.chat-messages{
flex: 1;
height:100%;
overflow: auto;
display: flex;
flex-direction: column-reverse;
}
.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }
/* temp. buttons for demo */
button { width: 12%; height: 44px; margin-left: 5%; vertical-align: top; }
/* begin - fix for hidden scrollbar in IE/Edge/Firefox */
.chat-messages-text{ overflow: auto; }
#media screen and (-webkit-min-device-pixel-ratio:0) {
.chat-messages-text{ overflow: visible; }
/* reset Edge as it identifies itself as webkit */
#supports (-ms-accelerator:true) { .chat-messages-text{ overflow: auto; } }
}
/* hide resize FF */
#-moz-document url-prefix() { .chat-input-text { resize: none } }
/* end - fix for hidden scrollbar in IE/Edge/Firefox */
<div class="chat-window">
<div class="chat-messages">
<div class="chat-messages-text" id="messages">
Long long content 1!<br/>
Long long content 2!<br/>
Long long content 3!<br/>
Long long content 4!<br/>
Long long content 5!<br/>
</div>
</div>
<div class="chat-input">
<textarea class="chat-input-text" placeholder="Type your message here..." id="inputs"></textarea>
<button onclick="addContent();">Add msg</button>
<button onclick="resizeInput();">Resize input</button>
</div>
</div>
Side note 1: The detection method is not fully tested, but it should work on newer browsers.
Side note 2: Attach a resize event handler for the chat-input might be more efficient then calling the updateScroll function.
Note: Credits to HaZardouS for reusing his html structure
You just need one CSS rule set:
.messages-container, .scroll {transform: scale(1,-1);}
That's it, you're done!
How it works: First, it vertically flips the container element so that the top becomes the bottom (giving us the desired scroll orientation), then it flips the content element so that the messages won't be upside down.
This approach works in all modern browsers. It does have a strange side effect, though: when you use a mouse wheel in the message box, the scroll direction is reversed. This can be fixed with a few lines of JavaScript, as shown below.
Here's a demo and a fiddle to play with:
//Reverse wheel direction
document.querySelector('.messages-container').addEventListener('wheel', function(e) {
if(e.deltaY) {
e.preventDefault();
e.currentTarget.scrollTop -= e.deltaY;
}
});
//The rest of the JS just handles the test buttons and is not part of the solution
send = function() {
var inp = document.querySelector('.text-input');
document.querySelector('.scroll').insertAdjacentHTML('beforeend', '<p>' + inp.value);
inp.value = '';
inp.focus();
}
resize = function() {
var inp = document.querySelector('.text-input');
inp.style.height = inp.style.height === '50%' ? null : '50%';
}
html,body {height: 100%;margin: 0;}
.conversation {
display: flex;
flex-direction: column;
height: 100%;
}
.messages-container {
flex-shrink: 10;
height: 100%;
overflow: auto;
}
.messages-container, .scroll {transform: scale(1,-1);}
.text-input {resize: vertical;}
<div class="conversation">
<div class="messages-container">
<div class="scroll">
<p>Message 1<p>Message 2<p>Message 3<p>Message 4<p>Message 5
<p>Message 6<p>Message 7<p>Message 8<p>Message 9<p>Message 10<p>Message 11<p>Message 12<p>Message 13<p>Message 14<p>Message 15<p>Message 16<p>Message 17<p>Message 18<p>Message 19<p>Message 20
</div>
</div>
<textarea class="text-input" autofocus>Your message</textarea>
<div>
<button id="send" onclick="send();">Send input</button>
<button id="resize" onclick="resize();">Resize input box</button>
</div>
</div>
Edit: thanks to #SomeoneSpecial for suggesting a simplification to the scroll code!
Please try the following fiddle - https://jsfiddle.net/Hazardous/bypxg25c/. Although the fiddle is currently using jQuery to grow/resize the text area, the crux is in the flex related styles used for the messages-container and input-container classes -
.messages-container{
order:1;
flex:0.9 1 auto;
overflow-y:auto;
display:flex;
flex-direction:row;
flex-wrap:nowrap;
justify-content:flex-start;
align-items:stretch;
align-content:stretch;
}
.input-container{
order:2;
flex:0.1 0 auto;
}
The flex-shrink value is set to 1 for .messages-container and 0 for .input-container. This ensures that messages-container shrinks when there is a reallocation of size.
I've moved text-input within messages, absolute positioned it to the bottom of the container and given messages enough bottom padding to space accordingly.
Run some code to add a class to conversation, which changes the height of text-input and bottom padding of messages using a nice CSS transition animation.
The JavaScript runs a "scrollTo" function at the same time as the CSS transition is running to keep the scroll at the bottom.
When the scroll comes off the bottom again, we remove the class from conversation
Hope this helps.
https://jsfiddle.net/cnvzLfso/5/
var doScollCheck = true;
var objConv = document.querySelector('.conversation');
var objMessages = document.querySelector('.messages');
var objInput = document.querySelector('.text-input');
function scrollTo(element, to, duration) {
if (duration <= 0) {
doScollCheck = true;
return;
}
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) {
doScollCheck = true;
return;
}
scrollTo(element, to, duration - 10);
}, 10);
}
function resizeInput(atBottom) {
var className = 'bigger',
hasClass;
if (objConv.classList) {
hasClass = objConv.classList.contains(className);
} else {
hasClass = new RegExp('(^| )' + className + '( |$)', 'gi').test(objConv.className);
}
if (atBottom) {
if (!hasClass) {
doScollCheck = false;
if (objConv.classList) {
objConv.classList.add(className);
} else {
objConv.className += ' ' + className;
}
scrollTo(objMessages, (objMessages.scrollHeight - objMessages.offsetHeight) + 50, 500);
}
} else {
if (hasClass) {
if (objConv.classList) {
objConv.classList.remove(className);
} else {
objConv.className = objConv.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
}
}
}
}
objMessages.addEventListener('scroll', function() {
if (doScollCheck) {
var isBottom = ((this.scrollHeight - this.offsetHeight) === this.scrollTop);
resizeInput(isBottom);
}
});
html,
body {
height: 100%;
width: 100%;
background: white;
}
body {
margin: 0;
padding: 0;
}
.conversation {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
position: relative;
}
.messages {
overflow-y: scroll;
padding: 10px 10px 60px 10px;
-webkit-transition: padding .5s;
-moz-transition: padding .5s;
transition: padding .5s;
}
.text-input {
padding: 10px;
-webkit-transition: height .5s;
-moz-transition: height .5s;
transition: height .5s;
position: absolute;
bottom: 0;
height: 50px;
background: white;
}
.conversation.bigger .messages {
padding-bottom: 110px;
}
.conversation.bigger .text-input {
height: 100px;
}
.text-input input {
height: 100%;
}
<div class="conversation">
<div class="messages">
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is a message content
</p>
<p>
This is the last message
</p>
<div class="text-input">
<input type="text" />
</div>
</div>
</div>
You write;
Now, consider this case:
The user scrolls to the bottom of the conversation
The .text-input, dynamically gets bigger
Wouldn't the method that dynamically sets the .text-input be the logical place to fire this.props.onResize().
To whom it may concern,
The answers above did not suffice my question.
The solution I found was to make my innerWidth and innerHeight variable constant - as the innerWidth of the browser changes on scroll to adapt for the scrollbar.
var innerWidth = window.innerWidth
var innerHeight = window.innerHeight
OR FOR REACT
this.setState({width: window.innerWidth, height: window.innerHeight})
In other words, to ignore it, you must make everything constant as if it were never scrolling. Do remember to update these on Resize / Orientation Change !
IMHO current answer is not a correct one:
1/ flex-direction: column-reverse; reverses the order of messages - I didn't want that.
2/ javascript there is also a bit hacky and obsolete
If you want to make it like a PRO use spacer-box which has properties:
flex-grow: 1;
flex-basis: 0;
and is located above messages. It pushes them down to the chat input.
When user is typing new messages and input height is growing the scrollbar moves up, but when the message is sent (input is cleared) scrollbar is back at bottom.
Check my snippet:
body {
background: #ccc;
}
.chat {
display: flex;
flex-direction: column;
width: 300px;
max-height: 300px;
max-width: 90%;
background: #fff;
}
.spacer-box {
flex-basis: 0;
flex-grow: 1;
}
.messages {
display: flex;
flex-direction: column;
overflow-y: auto;
flex-grow: 1;
padding: 24px 24px 4px;
}
.footer {
padding: 4px 24px 24px;
}
#chat-input {
width: 100%;
max-height: 100px;
overflow-y: auto;
border: 1px solid pink;
outline: none;
user-select: text;
white-space: pre-wrap;
overflow-wrap: break-word;
}
<div class="chat">
<div class="messages">
<div class="spacer-box"></div>
<div class="message">1</div>
<div class="message">2</div>
<div class="message">3</div>
<div class="message">4</div>
<div class="message">5</div>
<div class="message">6</div>
<div class="message">7</div>
<div class="message">8</div>
<div class="message">9</div>
<div class="message">10</div>
<div class="message">11</div>
<div class="message">12</div>
<div class="message">13</div>
<div class="message">14</div>
<div class="message">15</div>
<div class="message">16</div>
<div class="message">17</div>
<div class="message">18</div>
</div>
<div class="footer">
<div contenteditable role="textbox" id="chat-input"></div>
</div>
<div>
Hope I could help :)
Cheers