I'd like to know how could I get only the displayed controls in the screen in the very moment.
For example:
If I have a scrollbar which precludes the user from seeing everything
below the page, I'd like to make a selector which selects only what the user can see in his screen now. It would also be nice If I could select everything he does not see.
Is that possible? How?
Thanks
You could calculate the offsets (say, as the user scrolls) of what the user can see:
var top = $(window).scrollTop();
var bottom = top + $(window).height();
Then, you can see if an element is within this range.
$('*').each( function() {
var el = $(this);
var offsetTop = el.offset().top;
var inView = offsetTop >= top && offsetTop <= bottom;
el.addClass( inView ? 'in-view' : 'out-of-view' );
} );
Obviously there are some downsides performance wise to doing this. Depending what you want to do with this information you could select only inputs or whatever which might help.
I don't know if there is an easy or elegant solution to this. What you could do is calculate the offset position of all elements and the scroll offset to find out which elements are visible or not. This could become expensive if you have a lot of elements to check, but could work quite nicely otherwise.
Related
I'm making a slide scrolling page, and I'm trying to have it scroll like you're pulling a notecard up and with the next one right behind it.
To do this, I'm making them all fixed, and then moving their "top" position based off of scroll. But then I also need to make the body the size of the panel.
It's hard to describe what I'm doing, so here's the demo: https://codepen.io/NotDan/pen/vzraJE
Here's the particular piece of code that's causing my problem:
//what's going on here?
$(window).scroll(function(){
var panelNum = parseInt($(window).scrollTop()/$(window).height());//detemines panel number
var pixelMovement = ($(window).scrollTop())-(panelNum*$(".panel").height()); determines how many pixels to move the panel by
$('body').find(".panel:eq("+panelNum+")").css("top", -1*pixelMovement);
});
The problem is when the user scrolls quickly, the top position is not set accurately and there's some overhang. Again, hard to explain, but if you jump to the demo and scroll quickly you'll see what I mean.
Is there a more precise way of measuring scroll? Or is there a better way to do what I'm trying to? I've tried scrollmagic, and its "section wipe" feature is really close, but they bring the previous one up rather than move the current one up.
I tried making a condition to determine the panel number and everything started working.
var panelNum = 0;
var pixelMovement = 0;
$(window).scroll(function () {
pixelMovement = $(window).scrollTop() - panelNum * $(".panel").height(); // determines how many pixels to move the panel by
$("body")
.find(".panel:eq(" + panelNum + ")")
.css("top", -1 * pixelMovement);
if (Math.abs(pixelMovement) >= $(window).height()) {
panelNum++;
} else if (pixelMovement <= 0) {
panelNum--;
}
});
Here's the working demo: https://codepen.io/NotDan/pen/RYJeZq
Not sure what the method is or how its achieved. But I'm interested in knowing about it to possibly use it in an upcoming project. What I am referring to is when a block element is at a particular X/Y axis it seems it stops acting as though it were a fixed position element otherwise the element acts as a fixed position element.
I most commonly see this with navigation, where the header and footer are large and the element will stop acting as a fixed element when it reaches the bottom of the header or top of the footer
You can do something like this,
$(window).scroll(function(){
if ($(this).scrollTop() > 250){
$('#top').css('position','fixed');
}
else{
$('#top').css('position','static');
}
});
A better approach would be,
$(window).scroll(function(){
var top = $('#top');
if ($(this).scrollTop() > 250){
if(top.css('position') !== 'fixed'){
top.css('position','fixed');
}
}
else{
if(top.css('position') !== 'static'){
top.css('position','static');
}
}
});
There are plugins that will do this for you; this is one I've used before: link with relative success. Has great examples, too.
But if you want to do it yourself, it's not too difficult. The concept is slightly convoluted; if you change something's position to fixed, then it will not take up space, as it would if it was static.
When I came across this issue, I created a second item in the same place (or not, depending where you want it to appear), which is invisible. Then you implement a load/scroll event that checks if the window's scrollTop is greater than the top coordinate of the non-fixed object. If it is, show the fixed object.
Something like this:
$("#yourObject").each(function() { // The ID should be the FIXED object.
var $me = $(this);
var $heightRival = $("#anotherObject"); // This ID should be the non-fixed object.
$me.hide(); // Hide your fixed div.
$(window).bind("load scroll",function() {
var offset = $heightRival.offset(); // Get the document offset of the base object.
var height = $heightRival.outerHeight(); // Get the height of the base object.
if ($(window).scrollTop() > offset.top+height)
$target.show(); // Can be a fade in, slide in, whatever.
else
$target.hide(); // Can be a fade out, etc.
});
});
This is just a rudimentary code but it should set you on the right track.
Take a look at this plugin, or the others like it: http://www.orangecoat.com/stickyscroll
I just saw something very interesting in vk.com
Basically in profile page the right column is changing its width when the left column is gone of the view.
Example : http://vk.com/durov
Just scroll down a little bit to see it.
Every element in the DOM defines certain spatial offset properties that can be accessed from JavaScript, them being offsetTop, offsetLeft etc. which give you the value how much offset it has from its parent. You can also access the parent in question with a while loop which simply assigns the next parent until none are left ( you can add their offsets to the variables of the original offset to gain the "true" (x,y) offset) and the while loop exists when there are no more parent elements.
Then you do some simple logic to check whether or not it is located within the viewport (hint - use the page offset and width/height properties of the window object). Is this enough to go on or do you need a code sample?
You could do some hacks if you know where exactly your element will appear and its height (width is really of no consequence here), but that's a brute force approach. Simply checking the page Y offset and height against the predetermined "maxY" of the element you're trying to get offscreen to display a certain div to the right (like in the example given).
ADDENDUM: (Even more to the point)
This checks whether ANY part of the element is visible in the viewport:
function isVisibleInViewport(elem)
{
var y = elem.offsetTop;
var height = elem.offsetHeight;
while ( elem = elem.offsetParent )
y += elem.offsetTop;
var maxHeight = y + height;
var isVisible = ( y < ( window.pageYOffset + window.innerHeight ) ) && ( maxHeight >= window.pageYOffset );
return isVisible;
}
I tried writing it with clarity in mind. You can shorten it further if you wish. Even expand it to include logic if you wish to move to the side (x-axis, offsetLeft variable). And changes to your logic ought to be minimal if you want to check whether it's completely within view ( can't see a reason though).
My example usage
The logic to govern when to call this function goes beyond the scope of this, I hope you have some method in mind. My example:
function check()
{
var canvas = document.getElementById("webGlExperiments");
if(isVisibleInViewport(canvas))
document.getElementById("test").innerHTML = "It is!";
else
document.getElementById("test").innerHTML = "It is not!";
}
And then add an event listener to check for scrolls:
window.addEventListener("scroll", check, false);
For my example, you'll also need another element like a canvas with the id webGlExperiments, change it to something that suits you and your problem. This is the div in question I used in my test which you can adapt for yourself:
<div id="test" style="margin-top:100px;">Testing, testing!</div>
What would be computationally-efficient ways to select elements touching the top edge of browser window viewport as the page is scrolled?
See attached image. Green elements are selected because they are touching the top edge.
UPDATE
An example of how I'll use this is to fade elements that are going off-screen. There may be hundreds of them on the page. Imagine a page like Pinterest. Computing offset and scrollTop for hundreds of them at the rate of scroll event, even if throttled still feels really inefficient.
This is what I came up with. I think that it could be improved upon by caching the scrollTop values, but this is pretty good. I have included the framework for caching the boxtops, but not the implementation code. I have also only implemented scrolling down to hide divs. I have left reshowing them on upscroll as an exercise for you.
When the window is scrolled we get the last hidden div. We know that everything before this div is already hidden. Then use a 'while next element is off the screen' hide it. As soon as a div isn't off the screen we abort. Thus saving time from iterating through the entire list.
http://jsfiddle.net/kkv3h/2/
//track whether user has scrolled up or down
var prevScrollTop = $(document).scrollTop();
$(document).scroll(function() {
var currentScrollTop = $(this).scrollTop();
if (currentScrollTop > prevScrollTop) {
//down
var lasthiddenbox = $('.fadeboxhidden:last');
var nextbox = (lasthiddenbox.length > 0) ? lasthiddenbox.next('.fadebox') : $('.fadebox:first');
while (nextbox.length) {
console.log('box: ' + nextbox.offset().top + ' scroll: ' + currentScrollTop);
if (nextbox.offset().top < currentScrollTop) {
nextbox.animate({ opacity: 0 }, 3000).addClass('fadeboxhidden');
}
else { return; }
nextbox = nextbox.next('.fadebox:first');
}
} else {
//up
}
prevScrollTop = currentScrollTop ;
});
//create an object to hold a list of box top locations
var boxtops = new Object;
//gather all boxes and store their top location
$('.fadebox').each( function(index) {
//you may want to dynamically generate div ids here based on index. I didn't do this
//because i was already using the id for positioning.
var divid = $(this).prop('id');
boxtops[divid] = $(this).offset().top;
//console.log(boxtops[divid]);
});
I'm thinking the best way would be that you could mark elements you want to determine hit testing with by some class, say "hit-test-visible" or something. Then, for those elements, on the scroll event, you should be able to find their offset compared to the document - see jQuery offset, and then based on the scroll value, if the offset is less than the scroll, and the offset + element height is greater than the scroll offset, then the element should be "touching" the top edge.
I have the following code below that changes a div's position to fixed once an element scrollTop is greater than a number. I am trying to either amend this script or find a different solution so that the div will scroll between a range and STOP scrolling at some point ( so the div doesn't go off the page or overlap with footer elements.
I don't know if the right way is to say that IF scrollTop is greater than 150 then position=fixed, and if it's greater than 600 then position goes back to absolute, or if there's a better way, like distance from the bottom...
AND I use MooTools, not jQuery. I know there are a few jQuery options / plugins that do this. Thanks in advance!
window.onscroll = function()
{
if( window.XMLHttpRequest ) { // IE 6 doesnt implement position fixed nicely...
if (document.documentElement.scrollTop > 150) {
$('tabber').style.position = 'fixed';
$('tabber').style.top = '0';
} else {
$('tabber').style.position = 'absolute';
$('tabber').style.top = 'auto';
}
}
}
the script above is wrong on many levels.
don't use window.onscroll but window.addEvent("scroll", function() {});
cache selectors. using $("tabber") 3 times on each scroll when the element does not change is expensive.
just do var tabber = $("tabber") and reference that.
you don't need to do
$("tabber").style.position = ...
$("tabber").style.top = ...
do:
tabber.setStyles({
position: "fixed",
top: 12123,
right: 24
});
There are mootools plugins for this, eg scrollSpy by David Walsh: http://davidwalsh.name/mootools-scrollspy
it can allow you to set scripted events upon reaching various scrolling destinations or events, look at the examples.
or you could just write it yourself, eg, this took me 15 mins to do:
http://jsfiddle.net/dimitar/u9J2X/ (watch http://jsfiddle.net/dimitar/u9J2X/show/) - it stops being fixed when it reaches to 20 px of the footer. and then goes back to fixed if scrolling up again.