find number of child divs which can fit into parent divs - javascript

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>

Related

Setting div height to another div's height

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>

Only allow scrolling to stop on certain elements

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.

JS. Detect hidden child with parent overflow:hidden

I'm looking for simple way to detect, if child element of parent with overflow:hidden is visible within parent (it's not hidden by overflow).
I found something like this:
http://www.useallfive.com/thoughts/javascript-tool-detect-if-a-dom-element-is-truly-visible/
but i wonder maybe there is simpler solution.
Thanks in advance!
Assuming you want a vanilla js solution, try this:
function isVisible (parent, child) {
return !(
(child.offsetLeft - parent.offsetLeft > parent.offsetWidth) ||
(child.offsetTop - parent.offsetTop > parent.offsetHeight)
)
}
Basically "if the difference between the start of the parent element and the start of the child element is greater than the actual width or height of the parent, it's considered not visible"
Run the following snippet for an example:
var parent = document.getElementById('parent');
[].slice.call(document.querySelectorAll('.child')).forEach(function (child, i) {
console.log(i + ' is visible?', isVisible(parent, child));
});
function isVisible(parent, child) {
return !(
(child.offsetLeft - parent.offsetLeft > parent.offsetWidth) ||
(child.offsetTop - parent.offsetLeft > parent.offsetHeight)
)
}
* {
box-sizing: border-box;
}
#parent {
width: 200px;
height: 100px;
white-space: nowrap;
background: lightblue;
}
.child {
display: inline-block;
width: 75px;
height: 100%;
border: 1px solid green;
}
<div id="parent">
<div class="child">0</div>
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
</div>
You can do the following things:
check if the element has height and width that are bigger than 0px
check if the element display CSS property is not "none", preferably "block"
check if the element positioning is valid and if so, check if it´s "top" property is not bigger than the parent´s height
Same thing for left - if it is not bigger than parent´s width
When checking "width", check offsetWidth and clientWidth, those will show actual numbers as displayed to the client.
I had a similar requirement, but mine was a bit more complicated because the overflow: hidden element wasn't the first parent, it was like 5 or 6 elements away.
Just spend a whole day trying to do it with solutions from the internet(I've tried the repo you mentioned as well), but nothing worked.
So I've made this repo by myself (only JS, 2kb sized) https://github.com/LuizAsFight/is-element-visible.
It might help you, basically I just get the target element and climb the tree searching if any parent has a overflow:hidden, once I found it I get the parent's rect size, and check if the target element rect is inside the parent (visually, pixels)
for using it you just need to
import isVisible from 'is-element-visible';
const el = document.getElementById('id');
isVisible(el);
I hope it helps you, Best.

Javascript: prevent internal element "scrolling" in an element

I have a script that has a div with a width larger than its' parent, with the parent being set to overflow: hidden;. I have javascript that is setting the left positioning of the big div to create "pages". You can click a link to move between pages.
All of that works great, but the problem is if you tab from one "page" element to another, it completely messes up all the left positioning to move between the pages.
You can recreate this bug in the fiddle I set up by setting your focus to one of the input boxes on page ONE and tabbing until it takes you to page two.
I've set up a demo here.
The code that is important is as follows:
HTML:
<div class="form">
<div class="pagesContainer">
<div class="page" class="active">
<h2>Page One</h2>
[... Page 1 Content here...]
</div>
<div class="page">
<h2>Page Two</h2>
[... Page Content here...]
</div>
</div>
CSS:
.form {
width: 400px;
position: relative;
overflow: hidden;
border: 1px solid #000;
float: left;
}
.pagesContainer {
position: relative; /*Width set to 10,000 px in js
}
.form .page {
width: 400px;
float: left;
}
JS:
slidePage: function(page, direction, currentPage) {
if (direction == 'next') {
var animationDirection = '-=';
if (page.index() >= this.numPages) {
return false;
}
}
else if (direction == 'previous') {
var animationDirection = '+=';
if (page.index() < 0) {
return false;
}
}
//Get page height
var height = page.height();
this.heightElement.animate({
height: height
}, 600);
//Clear active page
this.page.removeClass('active');
this.page.eq(page.index()).addClass('active');
//Locate the exact page to skip to
var slideWidth = page.outerWidth(true) * this.difference(this.currentPage.index(), page.index());
this.container.animate({
left: animationDirection + slideWidth
}, 600);
this.currentPage = page;
}
The primary problem is that whatever happens when you tab from say, an input box on page one to something on page 2, it takes you there, but css still considers you to be at left: 0px;. I've been looking all over for a solution but so far all google has revealed to me is how to stop scrollbar scrolling.
Any help or suggestions would be appreciated, thanks!
P.S. The html was set up like this so that if javascript is disabled it will still show up all on one page and still function properly.
I updated your fiddle with a fix for the first tab with the form: http://jsfiddle.net/E7u9X/1/
. Basically, what you can do is to focus on the first "tabbable" element in a tab after the last one gets blurred, like so:
$('.form input').last().blur(function(){
$('.form input').first().focus();
});
(This is just an example, the first active element could be any other element)
Elements with overflow: hidden still have scrolling, just no scroll bars. This can be useful at times and annoying at others. This is why your position left is at zero, but your view of the element has changed. Set scrollLeft to zero when you change "pages", should do the trick.

jQuery Blind Effect on Dragged Divs

I've got a number of divs on a page that I can drag and drop around. I also implemented the blind effect on them so that I can minimize and maximize the content if i don't want to see it.
I've got a problem that if I have 3 items stacked on top of each other, vertically, and I move the bottom one to the right of the top one and minimize the top div, everything slides upwards - and the 3rd div that I moved up slides right off the screen!
I've tried a ton of stuff, like making divs use absolute positioning but that causes problems of divs not sliding upwards in some circumstances. Reordering the divs dynamically causes the divs to be thrown around the screen because of offsets and relative positioning.
I just want it so that when the user drags divs over to the left or right and an "earlier" div is minimized, all subsequent divs don't get moved.
Any suggestions on this one are greatly appreciated.
Edit 1:
The problem I'm having with the absolute positioning is as follows.
I start with A, B, and C in a vertical column. All items are expanded. I move B to the right side of A and C right under B. This gives me 2 columns (A being one and B,C being the other). With everything being absolute, if I try to close B, then C doesn't move up - rightfully so.
I tried then making things "selectively" absolute, thereby flipping between relative and absolute but I got into a problem with coordinates. If you have a relative position and left:100px and top:-50px, then flipping the position to absolute causes these coordinates to be interpreted within an absolute context. My control flies off the screen. I tried fixing this by getting the absolute coordinates using jQuery's offset function, however this returns the relative coordinates and I'm stuck. I tried to maintain the absolute coordinates myself, but it didn't work either for some reason. It's getting out of control :).
Javascript
This javascript bind is called when the page is loaded. I bind this function to a PNG arrow so that when the arrow is pressed, the content in the appropriate div expands and contracts.
$('.ArrowMargin').bind('click', function () {
var splits = this.src.split("/");
var action = "";
if (splits.length >= 2) {
var folder = splits[splits.length - 2];
var image = splits[splits.length - 1];
if (folder == "Images") {
if (image == "arrow_open.png") {
action = "close";
this.src = "Images/arrow_closed.png";
} else {
action = "open";
this.src = "Images/arrow_open.png";
}
}
}
var divs = document.getElementsByTagName("div");
if (action != "") {
var options = {};
for (var i = 0; i < divs.length; i++) {
var element = divs[i];
if (element.className.indexOf("Hideable") != -1) {
if (this.parentNode.parentNode == element.parentNode) {
if (action == "open") {
var jQueryObj = jQuery(element);
jQueryObj.show("blind", options, 500, null);
} else {
var jQueryObj = jQuery(element);
jQueryObj.hide("blind", options, 500, null);
}
break;
}
}
}
CSS
This is the CSS stuff where I set up some simple styles. I have some empty styles that I use to access divs based on class.
.ArrowMargin { float:right; margin:0 5px 0 0; }
.alpha { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px; }
.bravo { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px; }
.delta { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px;}
.charlie{}
.echo{}
HTML BODY
This is the body. It's just a number of divs that represent different pieces of content. The divs marked with the "Hideable" class are those that are jQuery blinded.
<body>
<div class="alpha">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar1</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable Center"></div>
</div>
<div class="bravo">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar2</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable charlie"></div>
</div>
<div class="delta">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar3</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable echo"></div>
</div>
</body>

Categories