Identify if child div reached top and bottom of parent - javascript

I've got a div inside another. The inner div (#items) scrolls within the parent div (#itemWrapper). I've got 2 arrows at the top an bottom.
I'm looking to hide the bottom arrow when the inner div reaches the bottom of the parent div and to hide the top arrow when the inner div reaches the top of its parent div.
My question is how do I get the distance / offset of the child div relative to its parent element to identify if it has reached the top or bottom?
Here is a FIddle
I tried this to find the position of the child div to its parent. But did not work.
EDIT:
html:
<div class="id="Wrapper">
<div class="row">
<img src="http://img42.com/p2OH4+" class="arrows" id="upArrow" height="128" width="128" />
<div id="itemWrapper">
<div id="items">
<div class="content">
<input type="image" src="http://img42.com/p1puE+" class = "thumb" />
</div>
<div class="content">
<input type="image" src="http://img42.com/p1puE+" class = "thumb" />
</div>
<div class="content">
<input type="image" src="http://img42.com/p1puE+" class = "thumb" />
</div>
</div>
</div>
<img src="http://img42.com/h1DoP+" class="arrows" id="downArrow" height="128" width="128" />
</div>
</div>
jQ:
('#subGrHolder').offset().top - $('#subGrHolder').parent().offset().top - $('#subGrHolder').parent().scrollTop();

I am going to put here what I said in comments in detail. Hope this is what you were looking for.
JavaScript:
var items = $('#items');
var upArrow = $('#upArrow');
var downArrow = $('#downArrow');
var scrollFactor = 100;
var scrollLeeway = 2;
items.on('scroll', onScroll);
upArrow.on('click', onUp);
downArrow.on('click', onDown);
function onScroll() {
if (items.scrollTop() === 0) {
upArrow.css('opacity', 0.1);
} else if (items[0].scrollHeight - items.scrollTop() <= items.height() + scrollLeeway) {
downArrow.css('opacity', 0.1);
} else {
upArrow.removeAttr('style');
downArrow.removeAttr('style');
}
}
function onUp(){ items.scrollTop(items.scrollTop() - scrollFactor); }
function onDown(){ items.scrollTop(items.scrollTop() + scrollFactor); }
jsFiddle.
Apologies if I have misunderstood your question entirely.

Related

Scroll child div while parent div is position sticky in vue JS

I have a use case for my landing page, where when user enters one of the <section> of the page, the section becomes fixed but the elements inside that <div> starts scrolling. Once the inner div is done scrolling the parent <section> should start scrolling as a normal section and should go away to the top with a scroll.
I've created a similar structure in the jsFiddle below. But not able to achieve the desired feature.
https://jsfiddle.net/8qm67ks9/2/
I wanted to achieve this behavior as presented on this website. Section SS below
HTML
... Other sections before
<section ref="stickyDiv" class="scroller py-sm-128">
<div class="">
<div ref="wrapper" #scroll="scrollerListener" class="wrapper-box">
<div class="d-flex px-sm-128">
<h3 class="section-heading">Access to all the exclusive opportunities</h3>
<div class="col-sm-6">
<img class="img-fluid"
src="https://res.cloudinary.com/stack-finance/image/upload/v1663728853/app-assets/v2/mask_group_590_e5gbgr.png"
>
</div>
</div>
<div class="d-flex px-sm-128">
<h3 class="section-heading">Access to all the exclusive opportunities</h3>
<div class="col-sm-6">
<img class="img-fluid"
src="https://res.cloudinary.com/stack-finance/image/upload/v1663728853/app-assets/v2/mask_group_590_e5gbgr.png"
>
</div>
</div>
<div class="d-flex px-sm-128">
<h3 class="section-heading">Access to all the exclusive opportunities</h3>
<div class="col-sm-6">
<img class="img-fluid"
src="https://res.cloudinary.com/stack-finance/image/upload/v1663728853/app-assets/v2/mask_group_590_e5gbgr.png"
>
</div>
</div>
</div>
</div>
</section>
... Other sections in the bottom
Vuejs
methods: {
...
listenBodyScroll(e) {
if (this.isMobile) {
return;
}
const stickyDiv = this.$refs.stickyDiv;
const wrapper = this.$refs.wrapper;
const dim = stickyDiv.getBoundingClientRect();
console.log(dim.y, dim.height, '---scrollTop');
if (dim.y <= 0) {
if (Math.ceil(dim.height) <= Math.abs(dim.y)) {
stickyDiv.style.position = 'relative';
stickyDiv.style.top = 'auto';
stickyDiv.style.height = 'auto';
wrapper.style.overflowY = 'hidden';
wrapper.scrollTop = wrapper.scrollHeight;
return;
}
wrapper.focus();
stickyDiv.style.position = 'sticky';
stickyDiv.style.top = '100px';
stickyDiv.style.height = '100vh';
wrapper.style.overflowY = 'auto';
}
},
scrollerListener({ target: { scrollTop, offsetHeight, scrollHeight, style } }) {
if (this.isMobile) {
return;
}
const stickyDiv = this.$refs.stickyDiv;
if ((Math.ceil(scrollTop) + offsetHeight) >= scrollHeight) {
stickyDiv.style.position = 'relative';
stickyDiv.style.top = 'auto';
stickyDiv.style.height = 'auto';
style.overflowY = 'hidden';
console.log('bottom!');
}
}
}
Vue direction v-scroll
Vue.directive('scroll', {
inserted(el, binding) {
const f = function(evt) {
if (binding.value(evt, el)) {
window.removeEventListener('scroll', f);
}
};
window.addEventListener('scroll', f);
}
});
There are no secret techniques in web development. Use your browser's inspect tool and check the website's HTML/CSS and see exactly how it works for yourself.
I did this myself and took some of the website's HTML and CSS and recreated a simple version you can see in this codesandbox
Basically, there are two divs of 50% width side by side that exist within the same parent div forcing them to be the same (very large) height. Once you scroll the parent div into view, the image container with position: sticky keeps it in the same relative screen position until you get to the bottom of its parent div where it will then scroll away with the rest of the parent div content.

How to simultaneously hide and show content and vice versa?

I have a problem and I need your help. I have several links (in <aside>) leading to several different menus (in <section>). On click over the link, only the relevant div in <section> is shown, the rest are hidden. This part is ok and working. What is not working is when I click over an image:
the current div (.menu) in <section> should be hidden;
the same picture (with bigger size) should be shown;
when you click once again over the big image, the big image should disappear and the current div in .menu (the one that was hidden on the first step) should appear one more time. Sort of toggling between content.
So if I click on a picture on the "second div" content, the same picture with bigger size should be show (the "second div" content should be hidden) and when I click once again over the big picture it should disappear and the "second div" content to be returned.
I tried with toggle() but had no success. Either I did not use it correctly, or it is not suitable for my case. This is where I managed to reach to.
I will really appreaciate your support - how to show only the hidden div, not all hidden div's. Right now, when you click on the big image it did not show the hidden div.
$(window).on("load", function() {
$("div.menu:first-child").show();
});
$(".nav a").on("click", function() {
$("div.menu").fadeOut(30);
var targetDiv = $(this).attr("data-rel");
setTimeout(function() {
$("#" + targetDiv).fadeIn(30);
}, 30);
});
var pictures = $(".img-1, .img-2").on("click", function() {
$("div.menu:active").addClass("hidden");
//how to reach out only the current, active div (not all div's in .menu)?
$(".menu").hide();
var par = $("section")
.prepend("<div></div>")
.append("<img id='pic' src='" + this.src + "'>");
var removePictures = $("#pic").on("click", function() {
$(this).hide();
$(".hidden").show();
});
});
.menu {
width: 100%;
display: none;
}
.menu:first-child {
display: block;
}
.row {
display: inline-block;
width: 100%;
}
.img-1,
.img-2 {
width: 120px;
height: auto;
}
<!doctype html>
<html>
<head>
</head>
<body>
<aside>
<ul class="nav">
<li>To first div
</li>
<li>To second div
</li>
<li>To third div
</li>
</ul>
</aside>
<section>
<div class="menu" id="content1">
<h3>First Div</h3>
<div class="present">
<div class="row">
<div>
<p>Blah-blah-blah. This is the first div.</p>
<img class="img-1" src="http://www.newyorker.com/wp-content/uploads/2014/08/Stokes-Hello-Kitty2-1200.jpg">
</div>
</div>
<div class="row">
<div>
<img class="img-2" src="https://jspwiki-wiki.apache.org/attach/Slimbox/doggy.bmp">
<p>Blah-blah-blah. This is the first div.</p>
</div>
</div>
</div>
</div>
<div class="menu" id="content2">
<h3>Second Div</h3>
<div class="present">
<div class="row">
<div>
<p>
Blah-blah-blah. This is the second div.
</p>
<img class="img-1" src="http://www.newyorker.com/wp-content/uploads/2014/08/Stokes-Hello-Kitty2-1200.jpg">
</div>
</div>
<div class="row">
<div>
<img class="img-2" src="https://jspwiki-wiki.apache.org/attach/Slimbox/doggy.bmp">
<p>
Blah-blah-blah. Yjis is the second div.
</p>
</div>
</div>
</div>
</div>
<div class="menu" id="content3">
<h3>Third Div</h3>
<div class="present">
<div class="row">
<div>
<p>
Blah-blah-blah. This is the third div.
</p>
<img class="img-1" src="http://www.newyorker.com/wp-content/uploads/2014/08/Stokes-Hello-Kitty2-1200.jpg">
</div>
</div>
<div class="row">
<div>
<img class="img-2" src="https://jspwiki-wiki.apache.org/attach/Slimbox/doggy.bmp">
<p>
Blah-blah-blah. This is the third div.
</p>
</div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
</body>
</html>
Sorry for the ugly sketch and pictures - it is only to get an idea what it should look like....
In general, it's poor form to ask on Stack Overflow how to code for a specific behavior. However, that takes some understanding of the libraries you're using, and what you are trying to achieve. Hopefully, my answer will help you better articulate and form your questions in the future.
Here's a fiddle for you: https://jsfiddle.net/hwd4b0ag/
In particular, I've modified your last click listener:
var pictures = $(".img-1, .img-2").on("click", function() {
var parentDiv = $(this).closest('div.menu').hide();
var blownUpPic = $("<img>").attr({
id: 'pic',
src: this.src,
'data-parent': parentDiv.attr('id')
})
.appendTo("section")
.on('click', function() {
$('#' + $(this).attr('data-parent')).show();
$(this).remove();
});
});
Now, let's review it!
First,
var parentDiv = $(this).closest('div.menu').hide();
In a jQuery listener, the this variable stores the current javascript DOM element that is the recipient of the event listener. In your case, it refers to an element that matches ".img-1, .img-2".
.closest(selector) will traverse up the DOM (including the current element) and find the first matching element for the provided selector. In this case, it finds your container div with class menu. Then we hide that div and save a reference to it in a variable.
Next, we create a full-sized version of the picture and assign it some attributes:
var blownUpPic = $("<img>").attr({
id: 'pic',
src: this.src,
'data-parent': parentDiv.attr('id')
})
We set the data-parent attribute to the id of our container div, so we have a reference back to it later.
We then add our image to the DOM:
.appendTo("section")
And declare a new click listener for it:
.on('click', function() {
$('#' + $(this).attr('data-parent')).show();
$(this).remove();
});
With $(this).attr('data-parent') we use the reference to our container div that we assigned earlier, and then retrieve that element by its id. We unhide the container div and remove the full-sized image.
All done!
There are better ways to code this, but I think this is a good next step for you that's analogous to your current code.

How to count the number of divs shown in each row when it depends on the screen resolution

I have the following code:
HTML
<div class="content" style="height: 30px;">
<div class="content-box" id="myOptions">
<div class="options-holder">
<input type="checkbox" name="11111" id="Bob_111">
<label for="111">Alex_1</label>
</div>
<div class="options-holder">
<input type="checkbox" name="22222" id="Bob_222">
<label for="222">Alex_2</label>
</div>
<div class="options-holder">
<input type="checkbox" name="33333" id="Bob_333">
<label for="333">Alex_3</label>
</div>
<div class="options-holder">
<input type="checkbox" name="44444" id="Bob_444">
<label for="444">Alex_4</label>
</div>
<div class="options-holder">
<input type="checkbox" name="55555" id="Bob_555">
<label for="555">Alex_5</label>
</div>
<div class="options-holder">
<input type="checkbox" name="66666" id="Bob_666">
<label for="666">Alex_6</label>
</div>
<div class="options-holder">
<input type="checkbox" name="77777" id="Bob_777">
<label for="777">Alex_7</label>
</div>
<div class="options-holder">
<input type="checkbox" name="88888" id="Bob_888">
<label for="888">Alex_8</label>
</div>
</div>
</div>
<div class="button-wrapper"> <a href="#" class="toggle-trigger" id="showMoreButton" style="display:none;">
<span data-collapse-text="Show less" data-expand-text="Show more" class="state up">Show more</span></a>
</div>
JAVASCRIPT
$.fn.myToggle = function () {
return this.each(function () {
var targetContainer = $(this),
targetBox = targetContainer.find('.content'),
targetTrigger = targetContainer.find('.toggle-trigger'),
targetState = targetTrigger.find('.state'),
contentBox = targetBox.find('.content-box'),
boxHeight = contentBox.outerHeight(),
optionHeight = targetBox.find('.options-holder').outerHeight();
$(window).on('resize', function () {
boxHeight = contentBox.outerHeight();
if (targetState.hasClass('down')) {
targetBox.stop(true, false).animate({
height: boxHeight
});
}
});
targetTrigger.on('tap', function () {
targetBox.stop(true, false);
if (targetState.hasClass('down')) {
targetState.text(targetState.data('expand-text'));
targetBox.animate({
height: optionHeight
});
} else {
targetState.text(targetState.data('collapse-text'));
targetBox.animate({
height: boxHeight
});
}
targetState.toggleClass('up down');
return false;
});
});
};
$(this.el).myToggle().on('click', '.checkbox-toggle', function (event) {
var toggle = $(this),
container = toggle.closest('.option-filter');
event.preventDefault();
container.find(':checkbox').prop('checked', toggle.hasClass('all'));
});
The problem is that the .option-holder divs don't fit in only one row in my .content-box div so i have to hide them and create a show more/less toggle button to show or hide the rest.
Everything works fine until the point that I only have the specific amount of .option-holder divs to fit only one line so i don't need the toggle button (the amount of divs comes dynamically from a server).
My current solution is to count the number of divs and show the toggle button only if they are more than 4 (in most screen resolutions i get 4 divs per row).
The problem is when the screen resolution is bigger and I get 5 or 6 per row.
If I have 6 divs per row but only 5 divs to show then the button still exists because I show it after 4 divs.
I know there are plenty easy fixes but I am not allowed to rewrite the code and change its logic so I have to find a way to count how many divs are shown each time in a row.
The code now works just by changing the height on div .content every time I click the button in order to show or hide the rest divs without giving the "not showing" divs any extra attributes e.g. style="display: none; to work with.
Any suggestions??
var divs = document.querySelectorAll('.options-holder'); //or getElementsByClassName... or just $('.options-holder').....
var x = divs.length;
var number_of_elements_in_first_row = 0;
for ( var i = 0; i < x; i++;) {
if ( divs[0].offsetHeight !== divs[i].offsetHeight ) {
divs[i].style.display = "none"; // hide divs[i]
/* or if you need only number of elements per row
add this instead hiding elements */
number_of_elements_in_first_row = i;
break;
}
}
if ( divs[0].offsetHeight !== divs[x].offsetHeight )
// add/show your MORE button... your function call
So, main idea is, if you have more than one row, last element will have different vertical position.
EDITED:
This code will go trough array of targeted divs and hide all divs that are not vertically aligned with the first one. This is not complete solution, more of idea how to handle this problem.
var divs = $('.options-holder');
for(var key in divs){
if (key > 0){
if (divs[0].offsetLeft == divs[key].offsetLeft) {
$("#showMoreButton").css("display", "block");
}
}
}
This is the solution needed as .offsetLeft is the same in every first div in each row..

How to create span content on mouse hover?

I want a slider with content. but the content should appear only when mouse hover. This is the code I'm using:
<div id="slideshow" style="width:560px; background:#999;">
<div id="slidesContainer">
<div class="slide">
<img src="js/1.jpg" />
<div> some text </div>
</div>
<div class="slide">
<img src="js/2.jpg" />
<div> some text </div>
</div>
<div class="slide">
<img src="js/3.jpg" />
<div> some text </div>
</div>
<div class="slide">
<img src="js/4.jpg" />
<div> some text </div>
</div>
</div>
</div>
and the jquery 1.8.3 lib and this code
$(document).ready(function(){
var currentPosition = 0;
var slideWidth = 560;
var slides = $('.slide');
var numberOfSlides = slides.length;
// Remove scrollbar in JS
$('#slidesContainer').css('overflow', 'hidden');
// Wrap all .slides with #slideInner div
slides
.wrapAll('<div id="slideInner"></div>')
// Float left to display horizontally, readjust .slides width
.css({
'float' : 'left',
'width' : slideWidth
});
// Set #slideInner width equal to total width of all slides
$('#slideInner').css('width', slideWidth * numberOfSlides);
// Hide left arrow control on first load
manageControls(currentPosition);
// manageControls: Hides and Shows controls depending on currentPosition
function manageControls(position){
if(position >= 4)
{
position=0;
currentPosition=0;
}
// Hide left arrow if position is first slide
if(position==0){ $('#leftControl').hide() } else{ $('#leftControl').show() }
// Hide right arrow if position is last slide
if(position==numberOfSlides-1){ $('#rightControl').hide() } else{ $('#rightControl').show() }
}
function Aplay(){
// Determine new position
currentPosition = currentPosition+1 ;
// Hide / show controls
manageControls(currentPosition);
// Move slideInner using margin-left
$('#slideInner').animate({
'marginLeft' : slideWidth*(-currentPosition)
});
setTimeout(function(){Aplay();},2000);
}
setTimeout(Aplay(),20000);
});
now the <div> some text </div> should appear only when mouse hover, example: http://www.backslash.gr/demos/contenthover-jquery-plugin/#demo4
CSS
<style type="text/css">
.title {visibility:hidden;}
</style>
HTML add a class title to all needed DIV elements
<div class="title"> some text </div>
Add this to your script
$('#slideInner').mouseover(function() {
$('#slideInner .title').css('visibility', 'visible');
});
$('#slideInner').mouseout(function() {
$('#slideInner .title').css('visibility', 'hidden');
});
Working jsBin
A simple solution would be to set up your 'slide' divs like this:
<div class="slide" onMouseOver="$('#TEXT1').css('display','block')" onMouseOut="$('#TEXT1').css('display','none')">
<img src="js/1.jpg" />
<div id="TEXT1" style="display:none;"> some text </div>
</div>
It's easy and i think this is the fastest solution, because require less jquery than the others (= keep your code clean)
Add the following class to every div which contain the description: class="slide-desc" to your HTML
Add var slideDesc = $('.slide-desc'); to your JS, which is just to fit your way of write jQuery, and slideDesc.hide(); (or $('.slide-desc').hide;, to use a faster way instead). This statement (it's better to call the .hide() method in first line of your JS, to avoid the description flashing while page is loading. Put it just after your declaration of variables.
Add somewhere in the JS these lines: $('#slidesContainer').hover(function(){
slideDesc.show();
});
You've done! :D

Can't avoid a div step on another div

Here is the problem site.
Let me explain in case I will solve it, so everyone can know about it.
I have two divs with the following html code
<div id="dataview" class="data-views" style="width:100%; height:580px; padding:0; border:solid; margin-bottom:10px;">
<div class="row-fluid">
<div class="col-md-8">
<div class="timeline"></div>
</div>
<div class="col-md-4">
<div id="map" class="map"></div>
</div>
</div>
</div>
The two divs I am talking about are the "col-md-8" and "col-md-4"
If you try to resize the window, then the second div will move on the bottom of the first one, but it covers everything else behind it. I try to play with css, but without a success. Any suggestion?
Edit:
Screenshots from the problem
How it is on full window: screen1
How it is after resize: screen2
Edit 2: I think something as a sollution but it is to complicated for me. Maybe someone could help. If the size of the screen is less than col-md-8 (so the second div will move to the bottom), I will change the height of the div (data-views) to 580+(height of map).
I tried this:
<script>
$(window).resize(resizemap);
function resizemap() {
var winWidth = $(window).width();
if (winWidth < 995) <--on 995px the map doesn't fit and move on the bottom.
{
var Hmap = $("#map").height();
$('#dataview').css("height", 580+Hmap);
}
}
</script>
The problem is that instead of change the size of the whole div, it changes the height only of the timeline. And the map moves again.
I figure how to do it. Here is the result:
HTML:
<div id= "dataview" style = "height:590px;border:solid;">
<div class="data-views" style="width:100%;height:580px;padding:0;margin-bottom: 10px;">
<div class="row-fluid">
<div class="col-md-8">
<div class="timeline"></div>
</div>
<div class="col-md-4">
<div id="map" class="map"></div>
</div>
</div>
</div>
</div>
And here is the javascript:
<script>
$(window).resize(resizemap);
window.onload = resizemap;
function resizemap() {
var winWidth = $(window).width();
if (winWidth < 995)
{
var Hmap = $("#map").height();
$('#dataview').css("height", 590+Hmap);
}
}
</script>
The only problem is that if I resize the window with the maximize button of the browser, the dataview height stay the same.

Categories