getting the difference between the top of a container a clicked element - javascript

I am trying to create a container that has children inside it all with the same class.
I'm trying to get the difference between the clicked div and the top of the container then animate that clicked div to the top of the container.
I have gotten mixed results.. when it worked it would animate then reanimate the position of the container back to its original position.
Can anyone give me some insight into why this is not functioning correctly?
Code below.
<style>
#contact-list-scroller {
position: absolute;
left: 0px;
top: 0px;
right: 0px;
bottom: 0px;
background: Silver;
overflow:scroll;
width: 200px;
}
#contact-list-scroller div {
background: white;
width: 150px;
height: 150px;
border: 1px solid Gray;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="position:relative; width:150px; height:100vh;">
<div id="contact-list-scroller">
<div class="test" id="contact_8965">stuff1</div>
<div class="test" id="contact_8966">stuff2</div>
<div class="test" id="contact_8967">stuff3</div>
<div class="test" id="contact_8968">stuff4</div>
<div class="test" id="contact_8969">stuff5</div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
</div>
</div>
<script>
$('.test').click(function(){
var contactTopPosition = $(this).offsetTop;
var containerTop = $('#contact-list-scroller').scrollTop();
var heightDifference = containerTop - contactTopPosition;
console.log(contactTopPosition + ' contactTop');
console.log(containerTop + ' containerTop');
console.log(heightDifference + ' heightDifference');
//$("#contact-list-scroller").scrollTop(contactTopPosition);
$("#contact-list-scroller").animate({scrollTop: heightDifference,
complete: $(this).unbind()
});
});
</script>

Is there a reason for giving every <div> inside of the container class="test"? If every child is a div, and has the same class, you can just use the cascading part of CCS, to target them. I also noticed you have a $(this).unbind() after the transition - you want each element to only be clickable once?
I've put together a snippet, assuming there's no specific need for the same class on all children, and commented out the unbind(). It uses element.position().top to find the visible distance between the element and its parent's top, then adds the parent's current scroll value.
$('#contact-list-scroller > div').click(function() {
var contactTopPosition = $(this).position().top;
var contactList = $(this).parent();
console.log(contactTopPosition + ' contactTop');
contactList.animate({
scrollTop: contactList.scrollTop() + contactTopPosition
//, complete: $(this).unbind() // Stops the function from running again after the first click, is that waht you want?
});
});
#contact-list-scroller {
position: absolute;
left: 0px;
top: 0px;
right: 0px;
bottom: 0px;
background: Silver;
overflow: scroll;
width: 200px;
}
#contact-list-scroller div {
background: white;
width: 150px;
height: 150px;
border: 1px solid Gray;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="position:relative; width:150px; height:100vh;">
<div id="contact-list-scroller">
<div id="contact_8965">stuff1</div>
<div id="contact_8966">stuff2</div>
<div id="contact_8967">stuff3</div>
<div id="contact_8968">stuff4</div>
<div id="contact_8969">stuff5</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>

Related

Horizantal scroll right to element

I am trying to scroll to right of each element, but could not calculate right of element and scroll to it.
var $scroller = $('.scroller');
$('button').on('click', function() {
var divIdx = $('input').val();
var scrollTo = $('#d' + divIdx)
.css('background', '#9f3')
.position().left;
console.log(scrollTo);
$scroller
.animate({
'scrollLeft': scrollTo
}, 500);
});
.scroller {
width: 300px;
height: 100px;
overflow-x: auto;
overflow-y: hidden;
direction: rtl;
}
.container {
position: relative;
/*important for the .position() method */
height: 100px;
width: 770px;
}
.container div {
height: 90px;
width: 60px;
float: right;
margin: 5px;
background: #39f;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="scroller">
<div class="container">
<div id="d1"></div>
<div id="d2"></div>
<div id="d3"></div>
<div id="d4"></div>
<div id="d5"></div>
<div id="d6"></div>
<div id="d7"></div>
<!-- ... -->
</div>
</div>
<button>Scroll to: </button> <input type="text" value="4" />
How to calculate right of element and scroll to right of element?
How to calculate right of element and scroll to right of element?
How to calculate right of element and scroll to right of element?
You can use this one.
var $scroller = $('.scroller');
$('button').on('click', function() {
var divIdx = $('input').val();
var div = $('#d' + divIdx);
div.css('background', '#9f3');
var divLeft = div.offset().left;
var divWidth = div.width();
var scrollerWidth = $('.scroller').width();
var scrollTo = divLeft - scrollerWidth + divWidth;
$scroller
.animate({
'scrollLeft': scrollTo
}, 500);
});
.scroller {
width: 300px;
height: 100px;
overflow-x: auto;
overflow-y: hidden;
}
.container {
position: relative;
/*important for the .position() method */
height: 100px;
width: 770px;
}
.container div {
height: 90px;
width: 60px;
float: left;
margin: 5px;
background: #39f;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="scroller">
<div class="container">
<div id="d1"></div>
<div id="d2"></div>
<div id="d3"></div>
<div id="d4"></div>
<div id="d5"></div>
<div id="d6"></div>
<div id="d7"></div>
<!-- ... -->
</div>
</div>
<button>Scroll to: </button> <input type="text" value="5" />
You just need to add some right margin and it's all done!
var $scroller = $('.scroller');
$('button').on('click', function() {
var divIdx = $('input').val();
var scrollTo = $('#d' + divIdx)
.css('background', '#9f3')
.position().left;
console.log(scrollTo);
$scroller
.animate({
'scrollLeft': scrollTo
}, 500);
});
.scroller {
width: 300px;
height: 100px;
overflow-x: auto;
overflow-y: hidden;
}
.container {
position: relative;
/*important for the .position() method */
height: 100px;
width: 600px;
}
.container div {
height: 90px;
width: 60px;
float: right;
margin: 5px;
background: #39f;
}
.container div:first-child {
margin-right: 240px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="scroller">
<div class="container">
<div id="d1"></div>
<div id="d2"></div>
<div id="d3"></div>
<div id="d4"></div>
<div id="d5"></div>
<div id="d6"></div>
<div id="d7"></div>
<!-- ... -->
</div>
</div>
<button>Scroll to: </button> <input type="text" value="4" />
There are two points to consider -
position().left gives you the value of the distance upto left edge of element only. So to get the value upto right edge, you can take left position of the element + the width of the element. $('#d' + divIdx).position().left + $('#d' + divIdx).width()
There should be additional one item's width white space after the last item which allow the scrollbar to move upto the end of the last item.

Is it possible to make parent element (relative) fill the height of its absolute positioned content?

I'd like my parent div to expand the height of the content, as my content will be dynamic. However, the content must be (I think) positioned absolutely so they can overlap each other vertically.
I've concluded I'll have to use JS to find the offset from the top to the bottom of the last element in the container, then set the height to that.
I'm currently doing something like this:
var lastElement = document.getElementById('three');
var bounds = lastElement.getBoundingClientRect();
var bottomOffset = bounds.top + $("#three").height();
$("#container").height(bottomOffset);
However this is clunky within my application, and the application of the height is not instantaneous, leading to a sluggy site.
Is there a better way?
var lastElement = document.getElementById('three');
var bounds = lastElement.getBoundingClientRect();
var bottomOffset = bounds.top + $("#three").height();
$("#container").height(bottomOffset);
body,
html {
height: 100% padding: 0;
margin: 0;
}
.absolute {
display: inline-block;
position: absolute;
background-color: blue;
width: 100px;
height: 100px;
}
#two {
top: 80px;
left: 120px
}
#three {
top: 160px;
left: 240px;
}
#container {
position: relative;
width: 100%;
;
background-color: yellow;
;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="absolute" id="one"></div>
<div class="absolute" id="two"></div>
<div class="absolute" id="three"></div>
</div>
View on JSFiddle
You can accomplish your result without any JS, but instead use CSS margin around the boxes to get the same result.
For the horizontal margin you can also use percentages (by request of OP).
For the vertical margins this will give unexpected results, since the percentage will still reference the width of the container (under "Property Values"), not the height.
html,body {height:100%; padding:0; margin:0;}
.container {
background-color: yellow;
}
.box {
display: inline-block;
width: 100px;
height: 100px;
margin-right: 2%;
background-color: blue;
}
.box.one {margin-top:0; margin-bottom:160px;}
.box.two {margin-top:80px; margin-bottom:80px;}
.box.three {margin-top:160px; margin-bottom:0;}
<div class="container">
<div class="box one"></div>
<div class="box two"></div>
<div class="box three"></div>
</div>
pixel-margin: https://jsfiddle.net/xzq64tsh/
percent-margin: https://jsfiddle.net/xzq64tsh/3/
Perhaps taking out the getBoundingClientRect() function, using jQuery instead might speed it up and simplify it a bit.
var lastElement = $('#three');
var bottomOffset = lastElement.offset().top + lastElement.height();
$("#container").height(bottomOffset);
body,
html {
height: 100% padding: 0;
margin: 0;
}
.absolute {
display: inline-block;
position: absolute;
background-color: blue;
width: 100px;
height: 100px;
}
#two {
top: 80px;
left: 120px
}
#three {
top: 160px;
left: 240px;
}
#container {
position: relative;
width: 100%;
;
background-color: yellow;
;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="absolute" id="one"></div>
<div class="absolute" id="two"></div>
<div class="absolute" id="three"></div>
</div>

Sticky floating bar : accurate position

I am looking for a better solution to sticky bar issue.
The '-----' between 2nd & 3rd box is a threshold from where the sticky bar should get display. When its displayed, it overlaps the 3rd box completely.
In the real solution, I have added css (margin-top) using jquery to push this element below; but the problem is it's lagging in Firefox. One can see this space for fractions of seconds on UI.
What is the best solution to achieve the output ( or avoid margin-top) ?
$(document).ready(function(){
function toggleDock() {
if($(window).scrollTop() >= $('.second').offset().top+$('.second').height()) {
$('.sticky').show();
}
else {
$('.sticky').hide();
}
}
$(window).bind('scroll',toggleDock);
});
.box {
border: 1px dotted red;
height: 100px;
width: auto;
margin: 20px 0;
}
.sticky {
height: 80px;
border: 1px dotted green;
margin: 20px 0;
display: none;
position: sticky;
top: 20px;
background: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body class='page docked'>
<div class='sticky'>
</div>
<div class='box'>
First
</div>
<div class='box second' >
2nd
</div>
-------
<div class='box'>
3rd
</div>
<div class='box'>
4th
</div>
<div class='box'>
5th
</div>
<div class='box'>
6th
</div>
<div class='box'>
7th
</div>
<div class='box'>
8th
</div>
</body>
UPDATED ANSWER
I think you need to keep your sticky div in the normal flow, and position: sticky is probably not the right choice here. Here is an example :
$(document).ready(function(){
function toggleDock() {
if($(window).scrollTop() >= $('.second').offset().top+$('.second').height()) {
$('.sticky').show();
}
else {
$('.sticky').hide();
}
}
$(window).bind('scroll',toggleDock);
});
.box {
border: 1px dotted red;
height: 100px;
width: auto;
margin: 20px 0;
}
.sticky {
height: 100px;
border: 1px dotted green;
margin: 20px 0;
top: 40px;
display: none;
background: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body class='page docked'>
<div class='box'>
First
</div>
<div class='box second' >
2nd
</div>
-------
<div class='sticky'>
</div>
<div class='box'>
3rd
</div>
<div class='box'>
4th
</div>
<div class='box'>
5th
</div>
<div class='box'>
6th
</div>
<div class='box'>
7th
</div>
<div class='box'>
8th
</div>
</body>
A nice document about CSS flow : http://marksheet.io/css-the-flow.html
position: sticky is not detailed there, but from this document, you'll see that a sticky element is positioned relatively to its containing element when it's visible, and becomes fixed (that is, it is taken out of the normal document flow) when its containing element is not visible :
A stickily positioned element is an element whose computed position value is sticky. It's treated as relatively positioned until its containing block crosses a specified threshold, at which point it is treated as fixed.
Hope this helps!
This is due to position: sticky. Using position: fixed alternatively will help you.
.sticky {
height: 80px;
border: 1px dotted green;
margin: 20px 0;
display: none;
top: 20px;
background: green;
position: fixed;
width: calc(100% - 40px); /* subtract the 20px taken by the left and right margins */
}

Dynamically added element function call not working

I have a problem with function call, when element is dynamically added.
Here is the link to code pen example. Function call - onclick - is from this button:
<button id="btnFocus" class="btnFocus" onclick="focusToDIV($(this))">Focus to DIV</button>
Function focusToDIV:
var focusToDIV = function(btnReference){
btnReference.parent().find("#div3").focus();
}
First element which contains this button is statically added. All the other elements can be added with button 'Add new'. With statically added element function focusToDIV gets called and div3 receives focus.
With dynamically added elements btnReference is not defined, that is why this error is thrown:
Uncaught TypeError: Cannot read property 'parent' of undefined
How to make this function work (put focus on div3 with click on btnFocus) with dynamically added elements? Why is btnReference not defined, if element is added dynamically to DOM?
var focusToDIV = function(btnReference){
btnReference.parent().find("#div3").focus();
}
var addNew = function(){
$("#divMain").append("<div class='divContainer' class='divContainer'> <div id='divInner' class='divInner'>" +
"<div id='div2' class='div2'> <div id='div3' class='div3' contentEditable='true'></div>" +
"</div></div> <button id='btnFocus' class='btnFocus' onclick='focusToDIV($(this))'>Focus to DIV</button> </div>");
}
.divMain{
height: 100vh;
width: 100vw;
}
.divContainer{
position: relative;
display: inline-block;
left: 20%;
top: 10%;
}
.divInner{
width: 300px;
height: 300px;
border: 1px solid black;
}
.div2{
position: relative;
left: 20px;
top: 20px;
width: 200px;
height: 100px;
border: 1px solid blue;
}
.btnFocus{
position: absolute;
top: 305px;
}
.div3{
position: relative;
left: 10%;
top: 10%;
width: 150px;
height: 80px;
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="divMain" class="divMain">
<button id="btnAdd" onclick="addNew()">Add new</button>
<div class="divContainer" class="divContainer">
<div id="divInner" class="divInner" >
<div id="div2" class="div2">
<div id="div3" class="div3" contentEditable="true"></div>
</div>
</div>
<button id="btnFocus" class="btnFocus" onclick="focusToDIV($(this))">Focus to DIV</button>
</div><!-- divContainer -->
</div><!-- divMain -->
You need to change the onclick method from focusToDIV() to focusToDIV($(this)).
As the element wasn't passed btnReference is undefined and you were calling a method on that undefined variable.
var addNew = function(){
$("#divMain").append("<div class='divContainer' class='divContainer'> <div id='divInner' class='divInner'>" +
"<div id='div2' class='div2'> <div id='div3' class='div3' contentEditable='true'></div>" +
"</div></div> <button id='btnFocus' class='btnFocus' onclick='focusToDIV($(this))'>Focus to DIV</button> </div>");
}

show hidden div over current div and next hide

<div id="home">
<div id="logo"> </div>
<div id="foot"> <div class="button"> CLICK ME</div>
<div class="button two"> CLICK ME</div> </div>
</div>
<div id="show">
TEST TEST TEST TEST
</div>
$('.button').click(function(){
$('#show').show();
})
#home {
width: 400px;
height: 400px;
background-color: #ccffff;
}
#logo {
width: 100%;
height: 300px;
background-color: #0099ff;
}
#foot {
width: 100%;
height: 100px;
background-color: #009999;
}
.button {
width: 90px;
height: 30px;
background-color: red;
margin-left: 50px;
}
i would like if i click RED .button then GREEN .show show me over the red button and if i click outside GREEN .show then this hide.
LIVE: http://jsfiddle.net/YeE4p/1/
Is this somewhat close to what you are looking for?
http://jsfiddle.net/neilheinrich/YeE4p/6/
I had to change the #show div to be absolutely positioned and use this javascript:
$('.button').click(function(){
var $show = $('#show');
var position = $(this).offset()
$show.css({
"left": position.left + "px",
"top":position.top + "px"
}).show();
$(window).bind("mousedown", function(e){
if (!($(e.target).attr("id") === "show")) {
$("#show").hide();
$(window).unbind("mousedown");
}
});
})
Try this out
http://jsfiddle.net/Quincy/YeE4p/4/
$('.button').click(function(event){
$('#show').show();
event.stopPropagation();
})
$('body').click(
function(){
$('#show').hide();
}
)

Categories