Prevent div scrolling up when bumped by div below it? - javascript

I asked a similar question yesterday but explained it poorly, and didn't specify my desire for a pure-CSS solution, which I think should be possible, so I'm trying again.
Basically, I have an issue where I have a div of scrollable messages and an input field below it. When I click a button, I would like the input field to be bumped up 100 pixels, without having the div of messages scroll as well.
Here is a fiddle that demonstrates the problem in its entirety
As you can see, when you click the "add margin" button, the messages div scrolls up as well. I would like it to stay where it was. Similarly, if you are slightly scrolled up so you can only see the second to last message, clicking the button should similarly retain that position upon click.
The interesting thing is that this behavior is "sometimes" preserved. For example, in some circumstances (which I can't quite deduce) the scroll position is retained. I would just like it to consistently behave as such.
window.onload = function(e) {
document.querySelector(".messages").scrollTop = 10000;
};
function test() {
document.querySelector(".send-message").classList.toggle("some-margin");
}
.container {
width: 400px;
height: 300px;
border: 1px solid #333;
display: flex;
flex-direction: column;
}
.messages {
overflow-y: auto;
height: 100%;
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
}
.some-margin {
margin-bottom: 100px;
}
<div class="container">
<div class="messages">
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
</div>
<div class="send-message">
<input />
</div>
</div>
<button onclick="test()">add margin</button>

This is a funny solution that you might like.
What we know about the div that it preserve only the top position of the scrollbar so if the height changed due to any reason the scrollbar will remain the same and this is what causes your issue.
As workaround you can flip the .messages 180 degree using transform: rotate(180deg) scaleX(-1); and flip back the .message to cancel flipping the content then the div will maintain the bottom scrollbar (which is top) automatically.
function test() {
document.querySelector(".send-message").classList.toggle("some-margin")
}
.container {
width: 400px;
height: 300px;
border: 1px solid #333;
display: flex;
flex-direction: column;
}
.messages {
overflow-y: auto;
height: 100%;
transform: rotate(180deg) scaleX(-1);
}
.message
{
transform: rotate(180deg) scaleX(-1);
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
}
.some-margin {
margin-bottom: 100px;
}
<div class="container">
<div class="messages">
<div class="message">hello1</div>
<div class="message">hello2</div>
<div class="message">hello3</div>
<div class="message">hello4</div>
<div class="message">hello5</div>
<div class="message">hello6</div>
<div class="message">hello7</div>
<div class="message">hello8</div>
<div class="message">hello9</div>
<div class="message">hello10</div>
<div class="message">hello11</div>
<div class="message">hello12</div>
<div class="message">hello13</div>
<div class="message">hello14</div>
<div class="message">hello15</div>
<div class="message">hello16</div>
<div class="message">hello17</div>
<div class="message">hello18</div>
<div class="message">hello19</div>
<div class="message">hello20</div>
</div>
<div class="send-message">
<input />
</div>
</div>
<button onclick="test()">add margin</button>

The normal behavior of the scrollbar it to be on the top, so when you set it to the bottom on page load, you have to maintain it your self because when ever the below content pushed it then div scroll will move to the top.
So I have two solutions for you:
Reverse the messages inside the messages div so the last message will be the first so the scroll will always be at the top.
I created a javascript function to scroll to the bottom of any element so you just call it whenever you want to scroll to the bottom.
function scrollbottom(e)
{
e.scrollTop = e.clientHeight;
}
Check the snippet
var elem = document.querySelector(".messages");
window.onload = function(e){
scrollbottom(elem);
}
function test() {
document.querySelector(".send-message").classList.toggle("some-margin");
scrollbottom(elem);
}
function scrollbottom(e)
{
e.scrollTop = e.clientHeight;
}
.container {
width: 400px;
height: 300px;
border: 1px solid #333;
display: flex;
flex-direction: column;
}
.messages {
overflow-y: auto;
height: 100%;
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
}
.some-margin {
margin-bottom: 100px;
}
<div class="container">
<div class="messages">
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
</div>
<div class="send-message">
<input />
</div>
</div>
<button onclick="test()">add margin</button>

You can do it the other way around, by giving the height to the .messages instead of giving it to the .container in this case it will not affect the messages div but if you are giving it to the .container it will push your div because the margin is inside the main div which have a height.
Check this snippet
function test() {
document.querySelector(".send-message").classList.toggle("some-margin");
}
.container {
width: 400px;
border: 1px solid #333;
display: flex;
flex-direction: column;
}
.messages {
height: 300px;
overflow-y: auto;
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
}
.some-margin {
margin-bottom: 50px;
}
<div class="container">
<div class="messages">
<div class="message">hello1</div>
<div class="message">hello2</div>
<div class="message">hello3</div>
<div class="message">hello4</div>
<div class="message">hello5</div>
<div class="message">hello6</div>
<div class="message">hello7</div>
<div class="message">hello8</div>
<div class="message">hello9</div>
<div class="message">hello10</div>
<div class="message">hello11</div>
<div class="message">hello12</div>
<div class="message">hello13</div>
<div class="message">hello14</div>
<div class="message">hello15</div>
<div class="message">hello16</div>
<div class="message">hello17</div>
<div class="message">hello18</div>
<div class="message">hello19</div>
<div class="message">hello20</div>
</div>
<div class="send-message">
<input />
</div>
</div>
<button onclick="test()">add margin</button>

The simple workaround is to set the previous scrollTop on toggle. Here I am using dataset to store previous scrollTop value, you can use variable either.
window.onload = function(e) {
document.querySelector(".messages").scrollTop = 10000;
}
function test() {
let state = document.querySelector(".send-message").classList.toggle("some-margin")
let div = document.querySelector(".messages");
if (state) {
div.dataset.top = div.scrollTop;
div.scrollTop += 100 // same as margin-bottom of .some-margin
} else {
div.scrollTop = div.dataset.top;
}
}
.container {
width: 400px;
height: 300px;
border: 1px solid #333;
display: flex;
flex-direction: column;
}
.messages {
overflow-y: auto;
height: 100%;
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
}
.some-margin {
margin-bottom: 100px;
}
<div class="container">
<div class="messages">
<div class="message">hello1</div>
<div class="message">hello2</div>
<div class="message">hello3</div>
<div class="message">hello4</div>
<div class="message">hello5</div>
<div class="message">hello6</div>
<div class="message">hello7</div>
<div class="message">hello8</div>
<div class="message">hello9</div>
<div class="message">hello10</div>
<div class="message">hello11</div>
<div class="message">hello12</div>
<div class="message">hello13</div>
<div class="message">hello14</div>
<div class="message">hello15</div>
<div class="message">hello16</div>
<div class="message">hello17</div>
<div class="message">hello18</div>
<div class="message">hello19</div>
<div class="message">hello20</div>
</div>
<div class="send-message">
<input />
</div>
</div>
<button onclick="test()">add margin</button>

I mean, the following is a CSS only solution that solves exactly your example:
.some-margin{margin-bottom:100px;}
.messages{margin-top:-100px;}
but I don't think it'll help you with your original problem of the virtual keyboard. But perhaps this can inspire someone else to a solution.
The main problem seems to be that the scrollbar is already doing exactly what you want:
Maintaining its position so the most recently read content is visible.
Except the scrollbar decides that you wanted to see the TOP of the currently visible content, not the bottom. (It's easier to see if you use numbers: https://jsfiddle.net/1co3x48n/ )
If you're willing to use javascript, there's all sorts of answers:
https://www.google.com/search?q=scrollbar+always+on+bottom+css+site:stackoverflow.com
Honestly, the fact that you can go through ~3+ pages of this and find only Javascript answers tells me that you're not going to find a CSS-only answer, certainly not one that's widely browser-compatible.

Create a container like in above examples, but then just alter the CSS when you start typing.
I dont set the scrollbar position, just move the whole message container upwards. So whatever your scrollposition might be, will stay the same. At least downwards, of course you will not be able to see the lines at the top.
You should be able to tweak the css to your needs. You could even go without JS if you use :focus or slightly different CSS setup, be creative :)
function activate(){
document.querySelector("#container").classList.toggle("active");
}
#container{
position:relative;
width:300px;
height:100vh;
max-height:230px;
overflow:hidden;
}
#messages{
position:absolute;
bottom:30px;
overflow:auto;
height:200px;
width:100%;
background:#eee;
}
#container.active #messages {
bottom:100px;
}
#send-message{
position:absolute;
border:1px solid #ccc;
bottom:0;
height:28px;
}
#container.active #send-message{
height:100px;
}
<div id="container">
<div id="messages">
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
</div>
<div id="send-message">
<input id="newmessage" onclick="activate()" type="text" />
</div>
</div>

you must add document.querySelector(".messages").scrollTop = 10000; to test function when add margin then scrollTop go to end
in load you set scrolltop and when you add margin or remove margin scrolltop didn't set again
change you script to like this
<script>
window.onload = function(e){
document.querySelector(".messages").scrollTop = 10000;
}
function test() {
document.querySelector(".send-message").classList.toggle("some-margin")
document.querySelector(".messages").scrollTop = 10000;
}
</script>

There seems to be a problem with the js toggle function. I've changed it to a simple hide & show. Also I've added a width on the input tag. You also need to add a position: absolute on the send-message div and bottom: 0 so it stays at the bottom. then place position: relative on the container so that the send message div stays within the container. This means that the message container won't affect the messages themselves and push them up.
window.onload = function(e) {
document.querySelector(".messages").scrollTop = 10000;
};
function test() {
var x = document.querySelector(".send-message");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
.container {
width: 400px;
height: 300px;
border: 1px solid #333;
display: flex;
flex-direction: column;
position: relative;
}
.messages {
overflow-y: auto;
height: 100%;
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
position: absolute;
bottom: 0;
}
.send-message input {
width:100%;
}
.some-margin {
margin-bottom: 100px;
}
<div class="container">
<div class="messages">
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">test</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
</div>
<div class="send-message">
<input />
</div>
</div>
<button onclick="test()">add margin</button>

None of the solutions provided really seem to work unfortunately. The problem with using onFocus for a textbox is that it won't apply if the textbox already has focus. The closest solution I've come up with so far is this:
componentDidMount() {
this.screenHeight = window.innerHeight;
let chatElem = document.querySelector(".conversations-chat");
window.addEventListener('resize', () => {
let diff = this.screenHeight - window.innerHeight;
chatElem.scrollTop += diff;
this.screenHeight = window.innerHeight;
});
}
However, this only seems to sometimes work. It works 100% of the time when clicking the textbox, but for some reason when closing the virtual keyboard it only works if it was scrolled up enough. Otherwise it resets to the same position every time (near the bottom, but not quite the bottom).
Not sure what's causing that. Will have to investigate more tomorrow I suppose. This is the closest one thus far though.

This could be one more approach that we can explore. If we don't want to play with scroll position and if it is ok if we shrink messages container without loosing usability. Then we can try something like below. For better understanding view the output in full page.
window.onload = function(e) {
document.querySelector(".messages").scrollTop = 10000;
};
function test() {
document.querySelector(".send-message").classList.toggle("some-margin");
document.querySelector(".messages").classList.toggle("scale");
}
.container {
width: 400px;
height: 300px;
border: 1px solid #333;
position: relative;
}
.messages {
position: absolute;
top: 0px;
left: 0px;
overflow-y: auto;
/* leave 1.4 rem space for input */
height: calc(300px - 1.4rem);
width: 100%;
}
.scale {
/*half of the 100 margin */
top: -50px;
/* scale = (totalHeight - 100px)/totalHeight */
transform: scaleY(0.64);
}
.send-message {
position: absolute;
bottom: 0;
width: 100%;
overflow: hidden;
}
.some-margin {
margin-bottom: 100px;
}
<div class="container">
<div class="messages">
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello</div>
<div class="message">hello4</div>
<div class="message">hello3</div>
<div class="message">hello2</div>
<div class="message">hello1</div>
</div>
<div class="send-message">
<input style="width:100%" />
</div>
</div>
<button onclick="test()">add margin</button>

A pure imperfect CSS approach that I can come up with is the following:
window.onload = function(e){
document.querySelector(".messages").scrollTop = 10000;
}
document.querySelector('button').addEventListener('click', test)
function test() {
document.querySelector(".send-message").classList.toggle("some-margin")
document.querySelector('.wrapper').classList.toggle('wrapper--transformed')
}
.container {
width: 400px;
height: 300px;
border: 1px solid #333;
display: flex;
flex-direction: column;
}
.messages {
position: relative;
overflow-y: auto;
height: 100%;
}
.wrapper {
display: block;
}
.wrapper--transformed {
transform: translateY(-100px);
margin-bottom: -100px;
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
}
.some-margin {
margin-bottom: 100px;
}
<div class="container">
<div class="messages">
<div class = "wrapper">
<div class="message">hello1</div>
<div class="message">hello2</div>
<div class="message">hello3</div>
<div class="message">hello4</div>
<div class="message">hello5</div>
<div class="message">hello6</div>
<div class="message">hello7</div>
<div class="message">hello8</div>
<div class="message">hello9</div>
<div class="message">hello1</div>
<div class="message">hello2</div>
<div class="message">hello3</div>
<div class="message">hello4</div>
<div class="message">hello5</div>
<div class="message">hello6</div>
<div class="message">hello7</div>
<div class="message">hello8</div>
<div class="message">hello9</div>
<div class="message">hello1</div>
<div class="message">hello2</div>
</div>
</div>
<div class="send-message">
<input />
</div>
</div>
<button>add margin</button>
The topmost section has a little problem when margin is there. The trick here is using negative margin and using translate to "scroll up" (not really a scroll, just an illusion) the wrapper div.

It can very simply be addressed with anchor names. Look at the JS fiddle at https://jsfiddle.net/gvbz93sx/
HTML Change
<a name="bottom"></a>
JS Change
document.location.hash = "#bottom";

Related

How do I toggle visibility of an entire dynamic row (flex wrap)?

Current Behavior
I have the following basic structure:
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content">Content</div>
</div>
<!-- ... dozens of .boxes ... -->
</section>
#containers is .display: flex; flex-wrap: wrap, so the number of boxes on any one row is dynamic. This is an important feature that must be maintained.
Here's a minimal working example:
$(document).ready(function() {
$(".collapsible").click(function() {
$( this ).next().slideToggle();
});
});
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
.collapsible:hover {
background: #aaf;
}
.content {
margin: 0.5em;
margin-left: 16px;
display: none; /* Initially collapsed */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box3">
<div class="collapsible"><h2>Box 3</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box4">
<div class="collapsible"><h2>Box 4</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box5">
<div class="collapsible"><h2>Box 5</h2></div>
<div class="content">Content</div>
</div>
</section>
</body>
I can easily use .slideToggle() to toggle visibility of a sibling (.content) underneath one of the clickable .collapsible divs:
$(".collapsible").click(function() {
$( this ).next().slideToggle();
});
Desired Behavior and Remarks
What I'd like is, on click of any .collapsible div in a row, the entire row will be toggled. That is, every .content div on the same horizontal row as displayed in the current viewport.
This must handle rows with dynamic number of columns, including viewport resizing. I'm flexible on the precise behavior, though.
Is this possible? And how much will it hurt?🙂 Changes to the document structure (such as adding some sort of row container) are OK, and I don't mind using some JS/jQuery code of course. The main thing I can't do is hard code the number of columns, as I need my site to remain responsive on vertical phones and fullscreen desktop browsers.
Maybe the jQuery slideToggle() function is not the best method.
Since a row will be the same height , you may look at a method to set size from 0 to another value. anybox resized will stretch other boxes on the same row.
here an example of the idea, setting a max-height from 0 to 200px and a transition.
it toggles a class.
$(document).ready(function() {
$(".collapsible").click(function() {
this.classList.toggle('slide');// plain javascript to toggle a class
});
});
*{box-sizing:border-box}
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
p{margin-top:0;}
.collapsible:hover {
background: #aaf;
}
.content {
margin:0 0.5em;
margin-left: 16px;
max-height:0; /* Initially collapsed */
transition:0.5s
}
.slide + .content{max-height:400px;/* which one got the class*/ color:darkred}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content"><p>Content</p></div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content"><p>Content</p><p>Content</p></div>
</div>
<div class="box" id="box3">
<div class="collapsible"><h2>Box 3</h2></div>
<div class="content"><p>Content</p></div>
</div>
<div class="box" id="box4">
<div class="collapsible"><h2>Box 4</h2></div>
<div class="content"><p>Content</p><p>Content</p><p>Content</p></div>
</div>
<div class="box" id="box5">
<div class="collapsible"><h2>Box 5</h2></div>
<div class="content"><p>Content</p></div>
</div>
</section>
</body>
What you are missing here , is to set the height of the row to the height of the taller box, unless they will all be the same height.
For this, you can look for every box at the same top offset that the one clicked, and look for the tallest innercontent of the .contents and use this height .
Here comes the idea looking where each are standing and resets a max-height rule, you can inspire from too:
// defaut : supposed to be with no class 'slide' set on html elements
$(document).ready(function () {
let boxes = document.querySelectorAll(".collapsible");
let contents = document.querySelectorAll(".content");
$(".collapsible").click(function (event) {
let arrayContent = [];//reset
let hide=false;
for (i = 0; i < contents.length; i++) {
contents[i].style.maxHeight = "min-content";
let index = i;
let heightC = contents[i].offsetTop;
let boxOffset = contents[i].parentNode.offsetTop + 1;
// a few infos .add/remove what is needed
arrayContent.push({
index,
heightC,
boxOffset
});
contents[i].setAttribute("style", "");// resets inline style
}
let rowOffset = this.offsetTop;
if(this.classList.contains('slide')) {
hide=true;
}
let classState = this.classList;
arrayContent.forEach(function (obj) {
if (obj.boxOffset == rowOffset) {
if(hide == true) boxes[obj.index].classList.add('slide');/* reset needed if window resized => rows reorganized ? */
boxes[obj.index].classList.toggle("slide");
} else {
boxes[obj.index].classList.remove("slide");
}
});
});
});
* {
box-sizing: border-box
}
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
p {
margin-top: 0;
}
.collapsible:hover {
background: #aaf;
}
.content {
margin: 0 0.5em;
margin-left: 16px;
max-height: 0;
/* Initially collapsed */
transition: max-height 0.5s!important
}
.slide~.content {
max-height: 400px;
/* which one got the class*/
color: darkred;
}
.collapsible:before {
content: attr(class)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible">
<h2>Box 1</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
<div class="box" id="box2">
<div class="collapsible">
<h2>Box 2</h2>
</div>
<div class="content">
<p>Content</p>
<p>Content</p>
</div>
</div>
<div class="box" id="box3">
<div class="collapsible">
<h2>Box 3</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
<div class="box" id="box4">
<div class="collapsible">
<h2>Box 4</h2>
</div>
<div class="content">
<p>Content</p>
<p>Content</p>
<p>Content</p>
</div>
</div>
<div class="box" id="box5">
<div class="collapsible">
<h2>Box 5</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
</section>
</body>

Avoid vertical scrollbar shift in container

In html, I have a panel with a fixed height which contains cards. It can contain one card or many cards. Hence, because the panel has a fixed height, it can be needed to have a scrollbar displayed in order to visualize all cards. This works properly with the property overflow: auto.
However, when the scrollbar is displayed, cards are shift. I would like to avoid that or at least hide this shift with a trick. I checked a lot of similar questions that suggests to use padding-left: calc(100vw - 100%); but it did not work since it is not the body scrollbar. The width of the card needs to be responsive according to the container's width.
Something that could work is to set the overflow: overlay and add a padding-right. However, this is not a standard and not compatible with firefox.
Here, you can find a reproduce example:
let flag = true;
const setHeight = () => {
if (flag) {
document.getElementById('container').style.setProperty('height', '100%');
} else {
document.getElementById('container').style.removeProperty('height');
}
flag = !flag;
};
document.getElementById('button').addEventListener('click', setHeight);
setHeight();
.panel-container {
height: 300px;
width: 510px;
padding: 8px 20px 0;
background-color: blue;
overflow: auto;
}
.card {
height: 86px;
width: 100%;
background-color: grey;
border-radius: 3px;
border: 1px solid red;
margin-bottom: 10px;
cursor: pointer;
}
.scrollbar::-webkit-scrollbar-track {
width: 14px;
}
.scrollbar::-webkit-scrollbar-thumb {
border-radius: 8px;
border: 4px solid green;
}
.scrollbar::-webkit-scrollbar-corner {
background-color: transparent;
}
.scrollbar::-webkit-scrollbar {
width: 8px;
height: 8px;
}
<button id="button">With/Without overflow</button>
<div id="container" class="panel-container scrollbar">
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
</div>
In fact, it was quite simple. Just play with the margin and set the overflow to scroll.
let flag = true;
const setHeight = () => {
if (flag) {
document.getElementById('container').style.setProperty('height', '100%');
} else {
document.getElementById('container').style.removeProperty('height');
}
flag = !flag;
};
document.getElementById('button').addEventListener('click', setHeight);
setHeight();
.panel-container {
height: 300px;
width: 510px;
padding: 8px 12px 0 20px;
background-color: blue;
overflow: scroll;
}
.card {
height: 86px;
width: 100%;
background-color: grey;
border-radius: 3px;
border: 1px solid red;
margin-bottom: 10px;
cursor: pointer;
}
.scrollbar::-webkit-scrollbar-track {
width: 14px;
}
.scrollbar::-webkit-scrollbar-thumb {
border-radius: 8px;
border: 4px solid green;
}
.scrollbar::-webkit-scrollbar-corner {
background-color: transparent;
}
.scrollbar::-webkit-scrollbar {
width: 8px;
height: 8px;
}
<button id="button">With/Without overflow</button>
<div id="container" class="panel-container scrollbar">
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
<div class="card">
<div class="card-left-container"></div>
<div class="card-middle-container"></div>
<div class="card-right-container"></div>
</div>
</div>

Head div with four sub div

Goodmorning developers,
I am new in frontend development. I'm stuck with the following problem:
I want to have a head div with 4 sub divs inside the head one. How can I do it with fitting the screen? (see sketch)
Kind regards
I think you need this please check:
See Fiddle Demo
.container {
border: 3px solid;
float: left;
width: 100%;
}
.custom_box {
float: left;
width: 100%;
}
.custom_box1 {
border: 3px solid;
float: left;
margin: 2%;
text-align: center;
width: 35%;
}
.custom_box2 {
border: 3px solid;
float: left;
margin: 2%;
text-align: center;
width: 55%;
}
<div class="container">
<div class="custom_box">
<div class="custom_box1">
<h1>1</h1>
</div>
<div class="custom_box2">
<h1>2</h1>
</div>
</div>
<div class="custom_box">
<div class="custom_box1">
<h1>1</h1>
</div>
<div class="custom_box2">
<h1>2</h1>
</div>
</div>
</div>
Here is the fiddle: https://jsfiddle.net/9Lum94me/
There are other ways as well with CSS floats, flexbox and table-cell which you can explore.
HTML:
<div class="container">
<div class="row">
<div class="column">Column 1</div>
<div class="column">Column 2</div>
</div>
<div class="row">
<div class="column">Column 3</div>
<div class="column">Column 4</div>
</div>
</div>
CSS:
.container{
border: 1px solid gray;
}
.row{
padding: 10px;
}
.row .column{
display: inline-block;
min-width: 45%; min-height: 150px; border: 1px solid gray; margin: 0 4% 0 0;
}
EDIT:
You will have to manage the widths of the columns as per needs.
A bootstrap responsive example without any style...(Except bordering).
Fiddle example
.content{
border:2px solid green;
height:100px;
width:92%;
margin:20px;
}
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<div class="row" style="border:1px solid blue;">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="row no-gutter-2">
<div class="col-lg-6 col-md-6 col-sm-6">
<div class="content">Header1</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-6">
<div class="content">Header2</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-6">
<div class="content">Header3</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-6">
<div class="content">Header4</div>
</div>
</div>
</div>
</div>
To test the responsiveness, plz re-size the browser window.
Above example is responsive on small, medium and large scale.
Please run the snippet on full page.

Aligning boxes(divs) of equal size in html symmetrically according to their number

I want to make an HTML page that contains a box at the top and a certain number of boxes being dynamically generated using jquery, the number of boxes at the bottom of top box can be 4 at max.
I want to align these boxes dynamically and symmetrically in the html page. I am using angularjs's ng-repeat to generate boxes. I want the sizes of the boxes to remain same but arrange them symmetrically on the html page.
currently i am using angular js to dynamically create boxes and align them using col-md class of bootstrap. but this makes the size of boxes to change when the number of boxes change.
html code
<div id="header-wrapper" class="container vscrolling_container">
<div id="header" class="container vscrolling_container">
<div id="logo">
<h1 class="page-head-line" id="visionh"><a>Vision</a></h1>
<p id="visionp"><a rel="nofollow">{{visiontext}}</a></p>
</div>
</div>
</div>
<div class="row" id="missionstart">
<div ng-class="missioncount[missions.length]" ng-repeat="mission in missions" style="opacity: 0.9">
<div class="dashboard-div-wrapper" ng-class="bkclr[$index]">
<h1 id="{{mission.id}}" style="color: #000">{{mission.missionInfo}}</h1>
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
</div>
<ul>
<li id="{{missioncontent.id}}" ng-repeat="missioncontent in mission.missionContent">
<p style="text-align: left">{{missioncontent.info}}</p>
</li>
</ul>
</div>
</div>
</div>
java script code
'use strict';
var mission_vision_mod = angular.module('myApp.mission_vision', ['ngRoute']);
mission_vision_mod.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/mission_vision', {
templateUrl: 'partials/mission_vision/mission_vision.html',
controller: 'mission_visionCtrl'
});
}]);
mission_vision_mod.controller('mission_visionCtrl', ['$scope','$http', function($scope, $http) {
$scope.visiontext = "Here is the content of vision";
$scope.bkclr = ['bk-clr-one','bk-clr-two','bk-clr-three','bk-clr-four'];
$scope.progressbar = ['progress-bar-warning','progress-bar-danger','progress-bar-success','progress-bar-primary'];
$scope.missioncount = ['col-md-0','col-md-12','col-md-6','col-md-4','col-md-3','col-md-2.5','col-md-2'];
$http.get('m_id.json').success(function(data){
$scope.missions = data;
$scope.len = data.length;
});
}]);
I have created a quick jsfiddle
HTML Content:
<div class="container">
<div class="header"></div>
<div class="content">
<div class="contentBox"></div>
<div class="contentBox"></div>
</div>
</div>
<br/><br/><br/>
<div class="container">
<div class="header"></div>
<div class="content">
<div class="contentBox"></div>
<div class="contentBox"></div>
<div class="contentBox"></div>
<div class="contentBox"></div>
</div>
</div>
Related CSS:
.container div {
height: 100px;
}
.header {
border: 1px solid black;
width: 75%;
margin: 5px auto;
}
.content {
text-align: center;
}
.contentBox {
border: 1px solid black;
width: 20%;
display: inline-block;
box-sizing: border-box;
}
Caution: I have used plain CSS for this demo.
Hope this helps you.
flexbox can do this
.wrap {
width: 80%;
margin: auto;
border: 1px solid black;
margin-bottom: 25px;
}
.top,
.item {
width: 100px;
height: 50px;
background: red;
}
.top {
display: inline-block;
}
.flex {
display: flex;
justify-content: space-around;
}
<div class="wrap">
<div class="flex">
<div class="top"></div>
</div>
<div class="flex">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
JSfiddle Demo

Display automatically set to block in JavaScript function

I have a problem in a JavaScript function in which i handle slider properties. I have 3 slide with class .slide and everytime i "choose" one of them i set it with .active-slide class. The problem is that also if the display property of active-slide is table, when i set this class, it automatically changes to block and i can't understand why.
P.s. i'm using also bootstrap.
I'm posting here the part of my code related to this problem:
HTML:
<div class="slider">
<div class="slide active-slide">
<div class="container up">
<div class="row">
<div class="col-md-5 slide-text">
<h1 class="slide-title">whatuwant</h1>
<p class="slide-par">whatuwant</p>
whatuwant
</div>
<div class="col-md-7" id="first-slide-image">
<img src="whatuwant"/>
</div>
</div>
</div>
</div>
<div class="slide">
<div class="container up">
<div class="row">
<div class="col-md-5 slide-text">
<h1 class="slide-title">whatuwant</h1>
<p class="slide-par">whatuwant</p>
</div>
<div class="col-md-7" id="second-slide-image">
<img src="whatuwant"/>
</div>
</div>
</div>
</div>
<div class="slide">
<div class="container up">
<div class="row">
<div class="col-md-5 slide-text">
<h1 class="slide-title">whatuwant</h1>
<p class="slide-par">whatuwant</p>
</div>
<div class="col-md-7 slide-image" id="third-slide-image">
<img src="whatuwant"/>
</div>
</div>
</div>
</div>
</div>
CSS:
.container {
width: 1024px;
}
.up{
display: table-cell;
vertical-align: middle;
max-width: 1024px;
max-height: 470px;
}
.row{
width: 1024px;
margin: 0 auto;
max-width: 1024px;
}
.slider {
position: relative;
width: 100%;
height: 470px;
}
.slide {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.active-slide{
display: table;
}
JavaScript:
$(document).ready(function() {
$('.buttonfornextslide').click(function() {
var currentSlide = $('.active-slide');
var nextSlide = currentSlide.next();
if(nextSlide.length === 0) {
nextSlide = $('.slide').first();
}
currentSlide.fadeOut(600).removeClass('active-slide');
nextSlide.fadeIn(600).addClass('active-slide');
});
$('.buttonforpreviousslide').click(function() {
var currentSlide = $('.active-slide');
var prevSlide = currentSlide.prev();
if(prevSlide.length === 0) {
prevSlide = $('.slide').last();
}
currentSlide.fadeOut(600).removeClass('active-slide');
prevSlide.fadeIn(600).addClass('active-slide');
});
});
I tried to simplify it to the maximum i could.
If the question wasn't clear: how can i hold the .active-slide display property to table?
jQuery automatically assigns "default" styles to elements when using fadeIn, slideDown, etc. Since the default style for divs are "block", that's what is being applied. Instead of using the class "active-slide", try specifically setting the CSS
....
prevSlide.fadeIn(600).css('display','table');
Refs:
jQuery bug
Fiddle - you can see that "display" block was in the class' style, and you have to literally add it after the fadeIn function.
In the fiddle, add
.css('display','block');
and run it, you'll see the changes

Categories