Count visible elements in a scrolled div - javascript

I would like to calculate how many items are visible (including the last one visible even if there is 10px shown) in a scrollable div to animate it in an AJAX callback transition.
Depending on screens sizes, that can vary and I'm looking for detect that.
The goal is to do something like that (but I don't know the lt(n))
$(".box:lt(n)")...
Example:
— http://jsfiddle.net/gy4uLu7x/

This code will do
$(function () {
var total_width = $(".box").length * $(".box").outerWidth(true);
$(".scroll").width(total_width);
var scroll = $('.scroll');
var viewport_w = scroll.parent().width();
var box1 = $('.box:first');
var boxw = box1.outerWidth(true);
var view_items = Math.ceil(viewport_w/ boxw);
console.log(boxw, viewport_w, view_items);
});
this code works on the fact that all boxes' (outer)width is equal. It calculates with of the immediate parent of .scroll which happens to be the body element and checks how many elements will fit into it. It converts the decimal part of the division result into and integer to accommodate for your _ even if there is 10px shown_ requirement. i.e., even 0.1 is converted into a 1.
Update
margin collapse won't happen :D

Calculate the width of scroll's parent (since this is the "viewport" in this case). Then count the boxes that start at a position less than above calculated width. This will work only in the above mentioned scenario.
var parentContainerWidth = $(".scroll").parent().width();
var containedBoxesCount = $('.box').filter(function () {
return $(this).offset().left <= parentContainerWidth;
}).length;
alert(containedBoxesCount);
Here is a demo

Related

Issue calculating table scrollbar location

So I have a script that adds a slight shadow to table edge where you can scroll, depending on the location of the scrollbar, but it sometimes doesn't work.
This is one part of it:
$('table').on('scrollstart scrollstop', function(){
if($(this).parent().hasClass('table-wrap')){
var elem = $(this),
elemBody = elem.find('tbody'),
elemParent = elem.parent('.table-wrap');
var scrolled = (elemBody.outerWidth() - elemParent.outerWidth() - elem.scrollLeft());
if(scrolled === 0){
elemParent.addClass('left_active');
elemParent.removeClass('right_active');
} else if(elem.scrollLeft() === 0) {
elemParent.removeClass('left_active');
elemParent.addClass('right_active');
} else {
elemParent.addClass('left_active');
elemParent.addClass('right_active');
}
}
});
This part sometimes I have to add "+1" to "elem.scrollLeft() --here---); to make it work.
var scrolled = (elemBody.outerWidth() - elemParent.outerWidth() -
elem.scrollLeft());
But then I noticed, some tables it helps and on others, it stops working. Meaning when I scroll to right the 'right_active' class will not disappear.
Any suggestions?
Have you tried including scrollbar width in your calculation for var scrolled?
var scrolled = (elemBody.outerWidth() - elemParent.outerWidth() - elem.scrollLeft());
I think you are on the right track but the width is probably not precise since .outerWidth() doesn't include scrollbar width. Hence, the maximum scroll width is always greater than the actual element width.

Set div width based on scroll position

The code:
http://codepen.io/anon/pen/EjrpMM
So, i'm working on an interesting problem. I am working with a 2000px HTML document, that has a modal placed ontop of it.
The width of the div lightbox is 80%, and it's sitting positioned fixed.
The goal is, when scrolling down the page, to control the width of the div based on the scroll position. At the bottom of the page, it's only a third in size.
I've had trouble figuring out the proper equation or formula for this, and was seeking help.
Currently, I've been trying to look at the window.pageYOffset, to add 2.5% to the div while increasing, and minus 2.5% when scrolling back up, to bring it back to it's 80% width.
However, something isn't working right. I was seeing if the community had any ideas to help solve the problem.
I'm not using any frameworks for this.
Here's my javascript:
var lightBox = document.getElementById('lightBox'),
count = 80,
num = window.pageYOffset;
document.addEventListener('scroll', function(e) {
var offset = window.pageYOffset;
num >= offset ? count += 2.5 : count -= 2.5;
num = offset;
lightBox.style.width = count + '%';
});
View the code here, in this codepen
http://codepen.io/anon/pen/EjrpMM
Thank you!
You just have to change
+= 2.5 and -=2.5 to += 0.7 and -= 0.7
When I checked your code I did this and it worked.
Scroll event fired once on scroll independently on how much you've scrolled. E.g. if you've scrolled 1px scrollbar, or scrolled 100px using mousewheel scroll event will be fired once.
So if you need stable results you will need to calculate your div width depending on scroll position (window.pageYOffset).
Check this codepen fork. I've assumed that in the end of page div width should be 50%.
Core part is:
var lightBox = document.getElementById('lightBox');
var upperBound = 80;
var lowerBound = 50;
var tickValue = (upperBound - lowerBound) / window.innerHeight;
document.addEventListener('scroll', function(e) {
var offset = window.pageYOffset;
var count = upperBound - offset * tickValue;
lightBox.style.width = count + '%';
});
Important note: for crossbrowser way to get innerHeight you can check this answer
This is a simple equation. Let f : scroll |-> f(scroll) be the function that gives you the width of your div. You want f(0) = 0.8, f(2000)= 1/3.
Let's say you want the progression to be linear so f(scroll) = a*scroll + b, you can easily deduce that b = 0.8 and a = (1/3 - 0.8)/2000 = -0.000233. Now for any scroll value, you can find the width of your div.
Now you can change the values when you want, f(scroll) = (minWidth-maxWidth)/pageLength * scroll + maxWidth.

How to get the real scroll height of div (say, inner text size)

I'm trying to make a auto-scrolling div that go to its top when it reaches the end. But it doesn't work...
function scrollPannel()
{
var pannel = document.getElementById('pannel');
if (typeof scrollPannel.count == 'undefined')
{
scrollPannel.count = 0;
}
else
{
scrollPannel.count += 2;
}
// trouble is here
if ((scrollPannel.count - pannel.scrollHeight) > pannel.clientHeight)
{
scrollPannel.count = 0;
}
pannel.scrollTop = scrollPannel.count;
setTimeout('scrollPannel()', 500);
}
HTML:
<div id='pannel' style="height:200px;overflow:auto" onmouseover="sleepScroll()">
<p>...</p><!-- long text -->
</div>
And after, I will need to find how to stop scrolling when "onmouseover" occures.
EDIT: I did not explained the problem clearly. In fact, I have tried something like:
if (scrollPannel.count > pannel.scrollHeight)
{
scrollPannel.count = 0;
}
The problem is that scrollHeight seems greater than div inner text. So it makes a lot of time to return to the top.
So I need an element property of which I could use the value to compare with my count variable. However I don't know Javascript a lot and I could not find anything. I hope it is as well simple as I think of it.
Try:
// calculate max scroll top position (go back to top once reached)
var maxScrollPosition = element.scrollHeight - element.clientHeight;
// example
element.scrollTop = maxScrollPosition;
That should do what you need.
You could try using the scrollHeight property.
https://developer.mozilla.org/en-US/docs/Web/API/element.scrollHeight
The solution I have involves jQuery, hope that's not a problem.
JavaScript:
var timeout;
function scrollPannel()
{
var divHeight = $("#pannel").height() / 2;
var scrollCount = $("#pannel").scrollTop();
var scrollHeight = $("#inside").height() - 20 - divHeight;
scrollCount += 2;
if ((scrollCount - scrollHeight) > 0)
{
scrollCount = 0;
}
$("#pannel").scrollTop(scrollCount);
timeout = window.setTimeout(scrollPannel(), 100);
}
function scrollStop() {
window.clearTimeout(timeout);
}
HTML:
<div id='pannel' onmouseover="scrollStop();" onmouseout="scrollPannel();">
<p id="inside"></p><!-- long text -->
</div>
Explanation:
jQuery's .height() of the inside element <p> gives us the actual height you're looking for, but it's not enough for reaching the bottom, since that happens before we reach the element's height. A little investigation shows that the "top" of scrollTop() is about half way inside the original div's height. You may need to play around with divHeight to get the exact results you're looking for.
Of course, I also included a method for stopping and continuing scrolling.
Good luck!
You should use scrollHeight property but to call it, you need to use an index like that:
$('#pannel')[0].scrollHeight;
If you set the scrollTop and scrollLeft to really high silly values they only ever get set as their maximum allowed values which I think is what you need? You can then use them to work out the scroll center if you wished.
See snippet example.
var mB = document.getElementById('myBox');
var mR = document.getElementById('myResult');
// Set the top and the left to silly values
mB.scrollTop = 99999999;
mB.scrollLeft = 99999999;
// They will only end up being set as their max
mR.innerHTML = "maxTop="+mB.scrollTop+"<br>maxLeft="+mB.scrollLeft;
// Now take the max values and divide by 2 to scroll back to middle.
mB.scrollTop = mB.scrollTop/2;
mB.scrollLeft = mB.scrollLeft/2;
#myBox{
overflow:auto;
}
#myContent{
border:1px solid black;
background-color:red;
}
<div id='myBox' style='width:300px;height:300px'>
<div id='myContent' style='width:500px;height:800px;line-height:800px;'><center>I am the center of the content</center></div>
</div>
<div id='myResult'></div>
I have got one solution....
function findMaxReach(){
let maxReach =0
document.querySelector('.YourElement').scrollLeft = 100000;
maxReach = document.querySelector('.YourElement').scrollLeft;
document.querySelector('.YourElement').scrollLeft = 0;
return maxReach
}

Getting Coordinates of an element on page scroll

I am having this problem where i have a set of 6 UL's having a common class x.Each of them consist of a specific section of the page.Now i have 6 menus that are related to each of the section.What i have to do is highlight the menu when its related section is in users view.
For this i thought that may be jQuery position(); or offset(); could have helped but they give the top and left of the element.I also tried using jQuery viewport plugin but apparently view port is big it can show more than one UL at a time hence i cant apply element specific logic here.I am not familliar to this but does anything changes of an element on scrolling?If yes then how to access it?
Please share your views.
Regards
Himanshu Sharma.
Is very easy to do it using jQuery and a dummy fixed HTML block that helps you find the current position of the viewport.
$(window).on("scroll load",function(){
var once = true;
$(".title").each(function(ele, index){
if($(this).offset().top > $("#viewport_helper").offset().top && once){
var index = $(this).index(".title");
$(".current").removeClass('current')
$("#menu li").eq(index).addClass('current')
once = false;
}
});
})
Check out a working example: http://jsfiddle.net/6c8Az/1/
You could also do something similar with the jQuery plugin, together with the :first selector:
$(window).on("scroll load",function(){
$(".title:in-viewport:first").each(function(){
var index = $(this).index(".title");
$(".current").removeClass('current')
$("#menu li").eq(index).addClass('current')
});
})
You can get the viewport's width and height via $(document).width() and $(document).height()
You can get how many pixels user scrolls via $(document).scrollTop() and $(document).scrollLeft
Combining 1 and 2, you can calculate where the viewport rectangle is
You can get the rectangle of an element using $(element).offset(), $(element).width() and $(element).height()
So the only thing left to you is to determine whether the viewport's rectangle contains (or interacts) the elements's rectangle
So the whole code may look like:
/**
* Check wether outer contains inner
* You can change this logic to matches what you need
*/
function rectContains(outer, inner) {
return outer.top <= inner.top &&
outer.bottom >= inner.bottom &&
outer.left <= inner.left &&
outer.right >= inner.right;
}
/**
* Use this function to find the menu related to <ul> element
*/
function findRelatedMenu(element) {
return $('#menu-' + element.attr('id'));
}
function whenScroll() {
var doc = $(document);
var elem = $(element);
var viewportRect = {
top: doc.scrollTop(),
left: doc.scrollLeft(),
width: doc.width(),
height: doc.height()
};
viewportRect.bottom = viewportRect.top + viewportRect.height;
viewportRect.right = viewportRect.left + viewportRect.width;
var elements = $('ul.your-class');
for (var i = 0; i < elements.length; i++) {
var elem = $(elements[i]);
var elementRect = {
top: elem.offset().top,
left: elem.offset().left,
width: elem.width(),
height: elem.height()
};
elementRect.bottom = elementRect.top + elementRect.height;
elementRect.right = elementRect.left + elementRect.width;
if (rectContains(viewportRect, elementRect)) {
findRelatedMenu(elem).addClass('highlight');
}
}
}
$(window).on('scroll', whenScroll);
Let's see if i understood well. You have a page long enough to scroll, and there is an element that when it appears in the viewport, you wanna do something with it. So the only event that's is triggered for sure on the time the element gets in the viewport is the 'scroll'. So if the element is on the page and the scroll is on the viewport, what you need to do is bind an action to the scroll event to check if the element is in the view each time the event is trigger. Pretty much like this:
$(window).scroll(function() {
check_element_position();
});
Now, in order for you to know if the element is in the viewport, you need 3 things. The offset top of that element, the size of the viewport and the scroll top of the window. Should pretty much look like this:
function check_element_position() {
var win = $(window);
var window_height = win.height();
var element = $(your_element);
var elem_offset_top = element.offset().top;
var elem_height = element.height();
var win_scroll = win.scrollTop();
var pseudo_offset = (elem_offset_top - win_scroll);
if (pseudo_offset < window_height && pseudo_offset >= 0) {
// element in view
}
else {
// elem not in view
}
}
Here, (elem_offset_top - win_scroll) represent the element position if there was no scroll. Like this, you just have to check if the element offset top is higher then the window viewport to see if it's in view or not.
Finally, you could be more precise on you calculations by adding the element height (variable already in there) because the code i just did will fire the event even if the element is visible by only 1 pixels.
Note: I just did that in five minutes so you might have to fix some of this, but this gives you a pretty darn good idea of what's going on ;)
Feel free to comment and ask questions

Find DOM elements at top and bottom of scrolling div with jQuery

I have a scrolling div containing list items. I have this boilerplate scroll event defined
$("#scrollingDiv").scroll(function(e) {
});
Inside of this scroll event function, how can I figure out which elements are at the top and bottom of the currently visible area?
You could try computing the positions of the list items with respect to the scrolling <div> and then scan the positions to see which ones match up with the scrollTop of the <div>.
Something like this perhaps:
var base = $('#scrollingDiv').offset().top;
var offs = [ ];
$('li').each(function() {
var $this = $(this);
offs.push({
offset: $this.offset().top - base,
height: $this.height()
});
});
$("#scrollingDiv").scroll(function() {
var y = this.scrollTop;
for(var i = 0; i < offs.length; ++i) {
if(y < offs[i].offset
|| y > offs[i].offset + offs[i].height)
continue;
// Entry i is at the top so do things to it.
return;
}
});
Live version (open your console please): http://jsfiddle.net/ambiguous/yHH7C/
You'd probably want to play with the fuzziness of the if to get something that works sensibly (1px visible hardly makes an element the top one) but the basic idea should be clear enough. Mixing in the height of #scrollingDiv will let you see which <li> is at the bottom.
If you have a lot of list items, then a linear search might not be what you want but you should be able to solve that without too much effort.

Categories

Resources