How do I make sure a whole tab is visible after scrolling? - javascript

After clicking the scroll button, the buttons inside tabs div are supposed to be scrolled to the left for a certain amount of pixels, but I sometimes end up with some buttons that are visible only just part of them.
So my question is how do I make sure that the start button inside the visible area of the tabs div is always visible 100%?
I have tried calculating with clientWidth, offsetWidth and many other variables and do not know how to proceed.
FYI, I do not know how many buttons will there be in advance and buttons have a different length.
JavaScript:
function moveLeft(){
var items = document.getElementsByClassName('item');
var wrapper = document.getElementById('tabs')
var clientWidth = wrapper.clientWidth;
var scrollWidth = wrapper.scrollWidth;
for (var i = 0; i < items.length; i++) {
items[i].style.left = '-'+clientWidth.toString()+'px'
}
}
CSS:
#wrapper {
width: 300px;
white-space: nowrap;
}
button {
display: inline-block;
}
#tabs {
width: 300px;
display: inline-block;
overflow: hidden;
}
.item {
position: relative;
left: 0;
transition: left 1s;
margin-right: 20px;
}
HTML:
<div id="wrapper">
<div id="tabs">
<button class="item">Tab1</button>
<button class="item">Tab2Tab2Tab2</button>
<button class="item">Tab3Tab3Tab3Tab3Tab3Tab3</button>
<button class="item">Tab4</button>
<button class="item">Tab5Tab5Tab5Tab5</button>
<button class="item">Tab6</button>
<button class="item">Tab7</button>
<button class="item">Tab8</button>
<button class="item">Tab9</button>
<button class="item">Tab10</button>
<button class="item">Tab11</button>
<button class="item">Tab12</button>
<button class="item">Tab13</button>
<button class="item">Tab14</button>
<button class="item">Tab15</button>
</div>
<button onclick="moveLeft()">></button>
</div>

I would recommend not to go the certain pixel size way but move it by a length of let's say 2 divs. This way you would have to have a counter to see how many times was the event fired to keep track of with which elements' width should you calculate.
So the code would look something like this.
var widthToMove = items[counter*2].clientWidth + items[counter*2+1].clientWidth + 40 //40 for the margin in between
for (var i = 0; i < items.length; i++) {
items[i].style.left = items[i].offsetLeft - widthToMove + 'px';
}
I would not use the clientWidth for this as when you move the elements to the left the width increases. Which would mean that it would move differently everytime it was fired.

Related

find number of child divs which can fit into parent divs

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>

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>

Keep document height

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>

Change image shown in fixed div when another div is in viewport

I have a fixed div containing an image that scrolls with the user from the top of the page. As new content divs enter the viewport I want the image to change.
I found a related piece of code that will change the image based on how far a user scrolls in pixels. This works, but only if the viewport is a specific size, else the image changes too early/late:
Example
I'm trying to modify this so that the change is instead based on when another div comes into view so that it works no matter the screen size (content div heights are set with relative units). I think this can be done if the other divs positions are saved to a variable and then used in place of the pixel values in the above code. However I can't seem to get this right, probably because I've not calculated the other div positions correctly.
$("#display1").fadeIn(1000);
$(window).scroll(function() {
var pos = $(window).scrollTop();
var first = $("#first").offset();
var second = $("#second").offset();
if (pos < first) {
hideAll("display1");
$("#display1").fadeIn(1000);
}
if (pos > first && pos < second) {
hideAll("display2");
$("#display2").fadeIn(1000);
}
etc...
});
function hideAll(exceptMe) {
$(".displayImg").each(function(i) {
if ($(this).attr("id") == exceptMe) return;
$(this).fadeOut();
});
}
You should try
getBoundingClientRect()
JS method, since It gets the position of the elements relative to the viewport. Check this answer: https://stackoverflow.com/a/7557433/4312515
Here is a quick proof of concept of changing a background image based on an element getting into view.
There are three divs. When the third div reaches the bottom of the viewport it will change the color of the background. When the third divs scroll out of the view again the background color is reset to its initial color.
Normally you should debounce the scroll event to prevent slowing down the UI. For this example I didn't debounce the event so you get a better sense of when the background is changed.
const
card3 = document.getElementById('card3'),
background = document.getElementById('background');
let
isCardVisible = false;
function checkDivPosition() {
const
cardTopPosition = card3.getBoundingClientRect().top,
viewportHeight = document.documentElement.clientHeight,
isInView = cardTopPosition - viewportHeight < 0;
if (isInView && !isCardVisible) {
background.style.backgroundColor = 'rebeccapurple';
isCardVisible = true;
} else if (!isInView && isCardVisible) {
background.style.backgroundColor = 'orange';
isCardVisible = false;
}
}
function onWindowScroll(event) {
checkDivPosition();
}
window.addEventListener('scroll', onWindowScroll);
body {
margin: 0;
}
.background {
height: 100vh;
opacity: .2;
position: fixed;
transition: background-color .3s ease-out;
width: 100vw;
}
.card {
border: 1px solid;
height: 100vh;
width: 100vw;
}
.card + .card {
margin-top: 5vh;
}
<div id="background" class="background" style="background-color:orange"></div>
<div class="card">
Card 1
</div>
<div class="card">
Card 2
</div>
<div id="card3" class="card">
Card 3.
</div>

Display images vertically until end of page, then continue horizontally within 100% height responsive div

I am using the following code which displays images vertically within a div that has 100% height and a auto width. I want the images to be displayed vertically until the end of the page and then start displaying horizontally. I want the content div width to be automated based on how many images are displayed but always have the 100% height. At the moment the images are displayed vertically until the page ends and then the rest of the images are hidden in the overflow.
#content {
min-height: 100%;
display: table;
width: auto;
overflow-y: hidden;
}
#thumbnailgrid {
overflow-y: hidden;
height: 100%;
max-height: 100%;
width: auto;
}
#thumbnailgrid img {
width: auto;
height: 24%;
}
Ok this is what I came up to, you might want to modify it a bit to fit your needs because this one doesn't keep track of margins or paddings, but it should help you solve your problem: http://jsfiddle.net/crbmL7sb/4/
To see it working try resizing the result window and clicking on "Run" again. You might want to add a window resize event listener as well.
Here's the HTML:
<div class="wrapper">
<div class="single">
<img src="http://placehold.it/200x200&text=A1" alt="placeholder" />
<img src="http://placehold.it/200x200&text=A2" alt="placeholder" />
<img src="http://placehold.it/200x200&text=A3" alt="placeholder" />
<img src="http://placehold.it/200x200&text=A4" alt="placeholder" />
<img src="http://placehold.it/200x200&text=A5" alt="placeholder" />
</div>
<div class="single">
<img src="http://placehold.it/200x200&text=B1" alt="placeholder" />
<img src="http://placehold.it/200x200&text=B2" alt="placeholder" />
<img src="http://placehold.it/200x200&text=B3" alt="placeholder" />
<img src="http://placehold.it/200x200&text=B4" alt="placeholder" />
<img src="http://placehold.it/200x200&text=B5" alt="placeholder" />
</div>
</div>
The Javascript:
var CL = function( vals ) {
columns = vals.columns;
var wHeight = $(window).height();
this.init = function() {
this.createColumns();
this.setBodyWidth();
}
this.createColumns = function() {
//loop through all the big columns
for(var i=0, l=columns.length; i < l; i++) {
images = $(columns[i]).find("img"); //get all the images in this column
columnHeight = 0; //set a flag to store the column height
var newDiv = $("<div />").addClass("inner"); //create an inner div inside the column
for (var p=0, n=images.length; p < n; p++) { //loop through all the images in this column
columnHeight += $(images[p]).height(); //add the height of the current image to the column height flag
if (columnHeight >= wHeight) { //compare the column height with the window height, if there's NOT enough room for the new image
newDiv.appendTo($(columns[i])); //append this column to the big parent div
var newDiv = $("<div />").addClass("inner"); //create a new column
columnHeight = $(images[p]).height(); //reset the column height flag
}
$(images[p]).appendTo(newDiv); //add the image to the current column
}
// THIS IS THE LINE I FORGOT
newDiv.appendTo($(columns[i]));
}
}
this.setBodyWidth = function() {
//when moving the images in the appropriate divs is done
var totWidth = 0;
$(".inner").each(function() {
totWidth += $(this).width(); //get the width of all the content
}) //and set it to the main wrapper to create the horizontal scrollbar
vals.wrapper.css({
width: totWidth
})
}
this.init();
}
//You need to somehow preload the images, or this will not work. Maybe add an overlay to cover the page while the images are being loaded and then fade it out. A simple window load even (like I commented out below) should work, but it appears to be broken on jsfiddle.
//$(window).load(function() {
var cl = new CL({
columns: $(".single"),
wrapper: $(".wrapper")
});
//});
And a little bit of CSS
img {
display: block;
}
.inner {
float: left;
}
img {
border: 4px solid white;
}
* {
box-sizing: border-box;
}

Categories