I have a series of elements that collapse ondragstart, and then uncollapse ondragend using Bootstrap's Collapse/Show classes
What I'm running into is that if the user is scrolled far enough down the page that removing that element's height makes the bottom of the page higher than the bottom of the viewport, the viewport is forced to scroll up to make up for it, which cancels the drag and calls ondragend, which then un-collapses the element and resizes the document to where it was, making it look like nothing happened.
My question is: How would I force the document/body to always be tall enough to prevent the auto-scroll?
Or, is it possible to somehow maintain the drag even through the scroll?
I have the min-height of the body set to 100vh, but that doesn't account for a page starting out to be taller than the viewport anyway.
Fiddle: https://jsfiddle.net/robertgreenstreet/jcodbv5n/21/
I see that your sections are wrapped by a form element.
<form>
<section</section>
<section</section>
<section</section>
<section</section>
</form>
What if, before you collapse any cards, you detect the form's actual height and force it to maintain that height using element.style.height?
Something like this:
function setHeight(element) {
element.style.height = element.offsetHeight + "px";
}
function clearHeight(element) {
element.style.height = "";
}
I haven't tried it, but I suspect it will do what you need.
Here's a little demo, it seems to work just fine.
function setHeight(element) {
element.style.height = element.offsetHeight + "px";
}
function clearHeight(element) {
element.style.height = "";
}
const wrapper = document.getElementById('wrapper');
const el2 = document.getElementById('el2');
const el3 = document.getElementById('el3');
function collapse () {
setHeight(wrapper);
el2.classList.add('collapse');
el3.classList.add('collapse');
setTimeout( () => clearHeight(wrapper), 2000);
}
function expand () {
clearHeight(wrapper)
el2.classList.remove('collapse');
el3.classList.remove('collapse');
}
#wrapper {
background-color: #ccc;
padding: 0.5rem;
}
#el1, #el2, #el3, #el4 {
border: 1px solid gray;
margin: 2px;
padding: 0.5rem;
background: white;
}
.el {
height: 40px;
}
.collapse {
height: 12px;
}
<body>
<button onclick="collapse();">Collapse el 2 and 3</button>
<button onclick="expand();">Expand el 2 and 3</button>
<div id="wrapper">
<div id="el1" class="el">el1</div>
<div id="el2" class="el">el2</div>
<div id="el3" class="el">el3</div>
<div id="el4" class="el">el4</div>
wrapper
</div>
</body>
Related
I have the following scenario:
I have an API which returns multiple div's. And in my UI, I have one parent div in which i need to show these div's. The condition is, if child div is overflowing parent, I need to show them on next page.
For eg: lets say my API is returning string like this:
<div class="ab0"></div>
<div class="ab1"></div>
<div class="ab2"></div>
<div class="ab3"></div>
<div class="ab4"></div>
and the parent div can fit in only ab0, ab1, ab2. Then I want to show these 3 div's 1st and when user click on '>' symbol I need to show ab3, ab4. Also if ab2 is partially overflowing and if I can show only overflowing part on next page, that will be great.
Is there any way I can do this.
Thanks in advance
A simple suggestion is to use the system's scroll functionality.
The system 'knows' how much it can show at once and as long as you can find out the height of the parent div you can move up and down the children (or part children if an exact number don't fit into the parent at once) using Javascript scrollTop.
const parent = document.querySelector('.parent');
const h = parent.offsetHeight;
let page = 0;
const lastPage = Math.floor((parent.scrollHeight + 1) / h);
function next() {
if (page < lastPage) {
page++;
parent.scrollTop = parent.scrollTop + h;
}
}
function prev() {
if (page > 0) {
page--;
parent.scrollTop = parent.scrollTop - h;
}
}
.parent {
height: 64px;
overflow: auto;
}
.parent div {
height: 2em;
border: 1px solid;
position: relative;
}
button {
font-size: 2em;
}
<div class="parent">
<div class="ab0">0</div>
<div class="ab1">1</div>
<div class="ab2">2</div>
<div class="ab3">3</div>
<div class="ab4">4</div>
</div>
<button onclick="prev();"><</button>
<button onclick="next();">></button>
</button>
I am developing a web application using AngularJS. I find myself in a situation where I have a bar (with the css I created a line) that must dynamically lengthen and shorten.
I know that JQuery scripts are sufficient to do this. For example, if my css is like this:
.my_line{
display:block;
width:2px;
background: #FFAD0D;
height: 200px; /*This is the part that needs to dynamically change*/
}
I could in the controller resize the line (of my_line class) simply with:
$(".my_line").css("height", someExpression*100 + 'px');
The thing is, I would like to dynamically resize the line based on the size of another div element (Or, in general, any HTML element of my choice).
I don't know how to get (at run-time) the size of a certain page element in terms of height.
Only in this way I would be able to create a line that dynamically lengthens or shortens as the size of a div (or some other element) changes!
How do you do this? So I will avoid writing hard-coded the measures but I want make sure that they vary as the dimensions of other elements on the page vary
I hope this is helping:
$(".my_line").css("height", $("#referenceElement").height()*5 + 'px');
.my_line{
display:inline-block;
width:2px;
background: #FFAD0D;
}
#referenceElement {
display:inline-block;
background: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="my_line"></div>
<div id="referenceElement">Hi, I'm 5 time smaller than the orange line!</div>
Here I am using the setInterval to track the div's height (you can do width as well) and storing it in a previousHeight variable and comparing it every interval
Then according to the comparison, it will determine if the height of the div has changed. If it has then it will change the height of the other div according to the height of the first div
You can create multiple variables and track multiple elements in the same setInterval
$(document).ready(function(){
var previousHeight = parseInt($("#my-div").css("height"));
setInterval(function(){ checkHeight(); }, 100);
function checkHeight() {
// Check height of elements here
var currentHeight = parseInt($("#my-div").css("height"));
if(currentHeight != previousHeight) {
previousHeight = currentHeight;
$("#dynamic-div").css("height", parseInt(currentHeight) + "px");
}
}
$("#button").click(function() {
$("#my-div").css("height", parseInt(previousHeight) + 5 + "px");
})
})
#my-div{
background: #000000;
height: 20px;
width: 20px;
}
#dynamic-div{
background: teal;
height: 20px;
width: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="my-div">
</div>
<button id="button">Increase div height</button>
<div id="dynamic-div">
</div>
I have a page with a div in the middle of content, whose height can change from javascript.
How can I control which way the page will scroll when the div height's changes? I want content to be always pushed down, never up.
Currently, the content is pushed down or up, depending on where the page scroll is when the button is clicked.
Here is a minimal example:
function toggle(ev) {
const div = document.querySelector("div");
if (div.style.height === "336px") {
div.style.height = "147px";
} else {
div.style.height = "336px";
}
}
body {max-width: 60em; margin: auto;}
<p style="background-color: coral; height: 400px;"></p>
<div style="background-color:grey; height: 147px;"></div>
<button href="#" onclick="toggle()">toggle div size</button>
<p style="background-color: olive; height: 3000px;"></p>
To make things clearer, I'm looking for a solution where the top paragraph never moves when the div expands, no matter where on the window the button currently is. i.e. the bottom paragraph should be move up/down as well as the button.
Try blurring the element (e.target.blur() on click handler) to make it inactive before updating the height of the content. Since the window no longer sees the button as active, it will no longer scroll the page to keep it in view.
const handleClick = e => {
e.target.blur()
updateScrollHeight()
}
function toggle(ev) {
const div = document.querySelector("div");
ev.target.blur();
if (div.style.height === "336px") {
div.style.height = "147px";
} else {
div.style.height = "336px";
}
}
document.querySelector("button").addEventListener("click", toggle);
body {max-width: 60em; margin: auto;}
<p style="background-color: coral; height: 400px;"></p>
<div style="background-color:grey; height: 147px;"></div>
<button href="#">toggle div size</button>
<p style="background-color: olive; height: 3000px;"></p>
If I understand correctly you want to scroll at the bottom every time you set the height
function toggle(ev) {
const div = document.querySelector("div");
if (div.style.height === "336px") {
div.style.height = "147px";
} else {
div.style.height = "336px";
}
div.scrollTop = div.scrollHeight
}
You can scroll to the selected element using window.scrollTo function.
<!doctype html>
<style>
body {max-width: 60em; margin: auto;}
</style>
<script>
function toggle(ev) {
const div = document.querySelector("div");
if (div.style.height === "336px") {
div.style.height = "147px";
} else {
div.style.height = "336px";
}
window.scrollTo(0, div.scrollHeight);
}
</script>
<p style="background-color: coral; height: 400px;"></p>
<div style="background-color:grey; height: 147px;"></div>
<button href="#" onclick="toggle()">toggle div size</button>
<p style="background-color: olive; height: 3000px;"></p>
The page is keeping the currently active element in view - i.e. the button that you clicked on - so the extra height will appear to move up or down depending on where the button is in relation to the screen.
What you want to do is to make the page look like it didn't scroll up after the element expanded. To do this we can just scroll the page back to the position it was before the element expanded - this makes it look as if the height expanded downwards.
1. Save the current position of the top of the page
var pagePosBeforeExpand = window.pageYOffset;
2. Scroll the page back to the position it was before we changed the height, if the div has been expanded:
// if we are increasing the height, then scroll to the previous top...
window.scrollTo({ top: pagePosBeforeExpand });
Note that this doesn't affect when you collapse the height again as you didn't ask for that in your question, however you could adapt this to suit in that scenario - e.g. you could simply move the scroll code to execute outside of the if.
Working Example:
function toggle(ev) {
// 1. Save the current position of the top of the page
var pagePosBeforeExpand = window.pageYOffset;
const div = document.querySelector("div");
if (div.style.height === "336px") {
div.style.height = "147px";
} else {
div.style.height = "336px";
// 2. scroll the page back to the position it was beforewe changed the height
window.scrollTo({ top: pagePosBeforeExpand })
}
}
body {max-width: 60em; margin: auto;}
<p style="background-color: coral; height: 400px;"></p>
<div style="background-color:grey; height: 147px;">Top of content</div>
<button href="#" onclick="toggle()">toggle div size</button>
<p style="background-color: olive; height: 3000px;"></p>
I've made a timeline using a sort of following this: https://www.w3schools.com/howto/howto_css_timeline.asp and set it to position: sticky. This is in a container called, div.timeContainer. Next to it, there's some text in a separate div. The idea is that the user scrolls down, reading the text on the right, while the timeline/overview on the left is in view.
The problem right now is that if I set the height of div.timeContainer, resizing the window means that the timeline will stop being in view/sticky around half-way through since the div on the right has become longer.
This (and variations) is what I have tried so far:
const historyContainer = document.querySelector("div.history").style.height
document.querySelector("div.timeContainer").style.height = historyContainer
I have prepared for you a simple example of assigning parent height to a child. An example in vanilla js.
let parent_div = document.querySelector('.parent');
let child_div = document.querySelector('.child');
let click_button = document.querySelector('input');
click_button.onclick = function(){
child_div.style.height = parent_div.offsetHeight + 'px';
};
.parent {
width: 100%;
height: 300px;
background-color: yellow;
}
.child {
width: 50%;
background-color: green;
}
<input type="button" value="click me to get the height of the child div">
<div class="parent">
<div class="child">
</div>
</div>
I've tried searching to see if there's already a question about this but can't find anything - so apologies if this is in fact a duplicate!
I've seen on some websites a feature where, when scrolling, the scroll stop point is forced to stop at a specific element rather than just wherever the user actually stopped scrolling.
I imagine this can be achieved via jQuery, but can't seem to find any documentation or help articles about it.
So, here's some example HTML...
<div id="one" class="block"></div>
<div id="two" class="block"></div>
<div id="three" class="block"></div>
With this as the CSS...
#one {
background: red;
}
#two {
background: green;
}
#three {
background: yellow;
}
.block {
width: 200px;
height: 100vh;
}
And what I'm looking to achieve is that when the user scrolls their browser from div 'one' to div 'two', once they've started scrolling over div 'two' and they then stop scrolling the browser automatically jumps them so that they see div 'two' in full, rather than a bit of the bottom of div 'one' and then most of div 'two' - I've definitely seen it done before but no idea how!
I hope this makes sense, and thanks in advance for any help or insight anyone can offer...
I don't remember too well, but I guess there are many ways to achieve what you want. One thing that came to my mind is to wrap around your divs and make a separate hidden div with full height. I did this adhoc solution below:
Once scroll approaches a threshold, I move to the div I should be looking at and vice versa. Here is a working solution FIDDLE:
HTML
<div id="phantom"></div>
<div id="wrapper">
<div id="one" class="block"></div>
<div id="two" class="block"></div>
<div id="three" class="block"></div>
</div>
CSS
#one {
background: red;
}
#two {
background: green;
}
#three {
background: yellow;
}
.block {
width: 200px;
height: 100vh;
}
#wrapper {
overflow:hidden;
position:fixed;
top:0px;
}
#phantom {
visibility:hidden;
}
JS
!function(){
//the array of divs
var divs = Array.prototype.slice.call(document.getElementsByClassName("block")), count = divs.length,
wrapper = document.getElementById("wrapper"),
phantom = document.getElementById("phantom"),
//the speed of scroll
scrollStep = 5,
//total length of phantom div
totalLength = Array.prototype.slice.call(wrapper.children).reduce(function(ac,d,i,a){return ac += d.clientHeight},0),
//store the animation frame here
currentFrame;
//wrapper is overflow hidden
wrapper.style.height = totalLength/count + "px";
//phantom has full height
phantom.style.height = totalLength + "px";
//add listener for scroll
window.addEventListener("scroll",function(){
//throttle the function
if(this._busy){return};
this._busy = true;
var that = this;
window.requestAnimationFrame(function(){
that._busy = false;
var heightOfDocument = Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),
totalScroll = Math.max(document.body.scrollTop,document.documentElement.scrollTop),
//which element should we look at?
whichElement = Math.round(totalScroll/heightOfDocument*count);
//if we are already around, don't do anything
if(divs[whichElement]._current){
return;
} else {
//cancel the last animation if any and start a new one
window.cancelAnimationFrame(currentFrame);
divs.forEach(function(d,i){delete d._current});
moveTo(divs[whichElement]);
}
});
},false);
//helper function to linearly move to elements
function moveTo(node){
node._current = true;
var top = node.offsetTop,
current = node.parentNode.scrollTop,
distance = top - current,
step = distance < 0 ? -scrollStep : scrollStep;
if(Math.abs(distance) < scrollStep){
node.parentNode.scrollTop = top;
return;
} else {
node.parentNode.scrollTop += step;
}
//store the current frame
currentFrame = window.requestAnimationFrame(function(){
moveTo(node);
});
}
}();
You obviously need to attach 'resize' event to update the values of the totalLength and set the correct new length on wrapper and phantom. You can also implement a easing function to modify the scrolling behavior to your taste. I leave them to you as homework.