I have a list of images on a page. As I scroll through the page I would like to show some options in a naviationbar of the image currently in the viewport. Therefore I need to get the image element currently in the viewport, is this possible ?
Jakob
Who says there's just one image in viewport? What would you like to do when there are many?
But otherwise you can always get the scroll position of your container with images as well as your images' top offset to see which one is currently in-view.
So these values will get you to your result
container scroll position
container visible client height
images' top offset
Using these values will make it possible to locate all images in the view regardless whether they're fully or partially displayed (at the top or bottom).
This is a simplified JSFiddle that gives red border around the first fully-in-the-view image. The code does this:
// get top positions and references to all images
var pos = $("img").map(function(){
var $this = $(this);
return {
el: $this,
top: $this.offset().top
};
}).get();
// provide document scrolling
$(document).on("scroll", function() {
$("img").removeClass("first-in-view");
var scroll = $(this).scrollTop();
var i = 0;
while(pos[i].top < scroll) i++;
pos[i].el.addClass("first-in-view");
}).scroll();
This should be optimised to only toggle class when it needs to. Otherwise we have flickering in every scroll. But it demonstrates how this can be done and you can get going from here.
IMPORTANT
It is utterly important that you attach your image position determining process on document load event and not the usually use DOM ready, because you have to wait for the document to load in order for your images to have final positions.
Related
I have a div with id "page-content", it does not have height or width, it just have a blank div.
I'm filling that div with content dynamically, so the div height is growing constantly, I'm making a chat, and i want to detect if I am at the bottom of the div or in the last 10% of the div total height, If true, scroll to the bottom
var box = $('#page-content');
if (box.scrollTop() > (box.height*0.90))
box.scrollTop(25000); // This is the top bottom
What I'm trying to do is, check if you are in the last 10% or less top bottom height of "#page-content" div (not when I'm reading "old messages" at the beginning of the Div), I have a function that appends new messages but I need to scroll down manually to see new messages...so i want to automatically scroll to the New bottom so i can see the new message :)
UPDATE:
function getChat() {
$.ajax({
type: "GET",
url: "refresh.php?lastTimeID=" + lastTimeID
}).done( function( data )
{
var jsonData = JSON.parse(data);
var jsonLength = jsonData.results.length;
var html = "";
for (var i = 0; i < jsonLength; i++) {
var result = jsonData.results[i];
html += '<span class="color-'+result.color+'"><b>'+result.usrname+'</b></span> <i class="fa fa-angle-right"></i> '+result.chattext+'<br>';
lastTimeID = result.id;
}
$('#page-content').append(html);
if(html!="")
{
// Here i need to check if the scroll position is in the bottom or in the last 10%
//then this to scroll to the top bottom (25000 is height limit)
$('.page-content').scrollTop(25000);
}
}); }
The trick is that inside the container for your messages (in your case the #page-content DIV), you can have an invisible placeholder div with some id set on to it.
In this demo JSFiddle, as you click on the anchor .addItem, after the new item is added to the container, the placeholder div is moved to the end of the container. This ensures at the same time that clicking on the .addItem brings the bottom of the container DIV into view (as it refers the id of the placeholder in its href attribute).
function scrollToBottom(container) {
// get all the child elements of the container
var children = container.children('.item');
// move the placeholder to the end of the container
$('#contentBottom').insertAfter(children.eq(children.length - 1));
}
Update
In order to determine your current scroll position, you should listen to scroll events in the container. Meanwhile, you should take into account the updated height value of the container when new messages arrive.
Check out this updated fiddle in which I'm checking if the current scroll position is beyond 60 % from the top to easily see the effect.
Note: If a new message comes when you are not scrolling, you can simply do $('.container').scrollTop(25000) in the same function/block of code that appends it to the container.
there is a trick in scrolling the page to bottom of DIV, i tried implementing it in this fiddle.
See $(window).height()+$(window).scrollTop() will always be equal to the total height(including paddings,margins) of children of the window, in our case it is equal to the $('#page-content').height()+margin/padding.
CSS:
div#page-content {
height:600px;
border:solid 1px red;
}
in our situation:
$(window).height()+$(window).scrollTop()=$('#page-content').height()+(margin/padding)=600px
so whenever we scroll it, we can attach an scroll() event to the div and easily check whether we are in in the last 10% or less top bottom height of "#page-content"
$(window).on('scroll',function(){
if($(window).height()+$(window).scrollTop()>=($('#page-content').height()*(.9))){
$(window).scrollTop($('#page-content').height()-$(window).height())
}
})
Good luck.
Since I did not make this, I don't want to take credit for it.
There is a jQuery plugin that makes anything that has a scroll bar scroll to a specific location or to an element. Since you want to scroll to a dynamic div, you can call this after you created the div and it will scroll to that location.
You can find the plugin over here.
You can find a demo of the plugin in action over here.
Hope this was what you are looking for.
-W
I am just trying to get the mouse hover div's position at the right according to the space around. Somehow I am able to do this in first two columns but not for other columns. May be my calculations while writing the condition state are wrong.
Can anyone please help?
JS Fiddle URL:
http://jsfiddle.net/mufeedahmad/2u1zr11f/7/
JS Code:
$('.thumb-over-team li').find('.cover').css({opacity:0});
$('.thumb-over-team li').on('mouseenter', function(){
var $this = $(this),
thisoffset = $this.position().left,
openDivId = $(this).find('.cover'),
thumbContainer = '.thumb-over-team',
speedanim = 200;
if(thisoffset + openDivId.outerWidth() >= $(thumbContainer).outerWidth()){
//thisoffset = $(thumbContainer).outerWidth() - openDivId.outerWidth() - 212;
thisoffset = thisoffset - openDivId.outerWidth()+10;
openDivId.stop().css({'z-index':'9999'}).animate({'opacity':'1', 'left':-thisoffset}, 200);
}else{
openDivId.stop().css({'z-index':'9999'}).animate({'opacity':'1', 'left':'212px'}, 200);
}
}).on('mouseleave', function(){
$(this).find('.cover').stop().css({'z-index':'-1'}).animate({'opacity':'0', 'left':'200px'}, 200);
});
$('.close-parent').on('click', function(){
$(this).parents('.cover').stop().css({'z-index':'-1'}).animate({'opacity':'0', 'left':'200px'}, 200);
});;
In your first conditional, try to calculate the position of the offset as:
thisoffset = ($(thumbContainer).outerWidth() - openDivId.outerWidth() - thisoffset);
That way, you're adjusting the appearing square (.cover) when it doesn't fit inside the container, to be as close possible to its rightmost edge: (maximum width - appearing square width - current li position)
Calculated this way, you can animate it with the new offset in positive:
openDivId.stop().css({'z-index':'9999'}).animate({'opacity':'1', 'left':thisoffset}, 200);
See it working here.
For elements that "almost" fit, the current system isn't completely precise because of what I already pointed out in my previous comment: the appearing square, even if it were at 0 opacity, would still be inside the containing element (($(thumbContainer)) or .thumb-over-team) and it would add its width to the total width of the container.
So your conditional may think that there's enough available space in the container to make the expandable element fit, but that would go out of the screen. As an example, notice that there's a horizontal scrollbar from the very beginning, caused by this effect, where your containing .thumb-over-team element doesn't fit in the screen.
But I would say that more precision in this point would require a fresh new approach to your system where the appearing .cover elements were out of the containing ul .thumb-over-team
Fresh take on the problem, essentially based on the main issue: the expandable text block (.cover) used to add its width to the container (.thumb-over-team). This altered the calculations on available container space, and made the text containers go off screen.
The solution is to make sure the expandable .cover elements aren't contained inside the .thumb-over-team element, so they won't impact the calculations on available width.
Here is a JSFiddle containing this new approach: link.
Explanation of how it works:
The idea was to create a separate element called .cover-container and let's put all the expandable .cover elements in there.
We want to associate every image in the li elements in .thumb-over-team with their appropriate .cover (so the first image triggers the first .cover to show, the second image would show the second cover, and so on.) We achieve is by finding out the index of the element that triggered the event:
thisLiIndex = $this.index() + 1
And then selecting the cover in the matching position:
openDivId = $('.cover-container .cover:nth-child(' + thisLiIndex + ')')
The expandable covers shouldn't interfere with the mouseenter or mouseleave events of .thumb-over-team, so we make it to ignore mouse events via CSS:
.cover-container{pointer-events:none;}
Changing from one image to another would automatically trigger new events, so the expanding covers stay visible when the mouse stays on the images, but close automatically when the mouse exits them.
Since the covers are now outside of $(thumbContainer), openDivID.outerWidth() does not alter $(thumbContainer).outerWidth(), and we can use that safely in our positioning.
If I understand the placement that you want, for covers that fit, the position is the current offset (position of the li element that triggered the event) plus the width of the image and some subtle margin
imageWidth + rightSeparation + thisoffset
And for covers that won't fit inside of the screen, we keep them just inside of the screen
thisoffset = $(thumbContainer).outerWidth() - openDivId.outerWidth();
I have seen some scrolling effects for example on Google SketchUp's site, their banner is initially "built into the page" and then it seems to pop out and remain stuck at the top after a certain position down (scrolling).
Google Plus seems to have some special effects as well, like changing the banner entirely once the scrolling has reached a certain position.
Attached is what I am trying to accomplish. A square logo is on the right, and then when the page is scrolled down, the logo starts to scale to the same height as the banner/header / fade and then becomes a word rather than the image.
What am I looking at here? jQuery or javascript? How do I track the scrolling and connect the two?
Is it what you want to achieve?
http://jsfiddle.net/agdbd8x6/15/
If so, it is quite easy. If you use jQuery, attach 'scroll' event handler and check current scroll position. Show the image only with zero scroll position:
var img = $('#image');
var txt = $('#text');
$(".container").scroll(function(){
txt.text('Scroll position = ' + $(this).scrollTop());
var showImage = $(this).scrollTop() == 0;
if (showImage){
img.css('display', 'inline');
txt.hide();
}
else{
img.hide();
txt.css('display', 'inline-block');
}
});
I am looking to create a scrolling effect similar to that shown here: http://www.seaham-hall.co.uk/
However I am unable to achieve the desired effect, and inspecting the sites code gives me no hints. Quite difficult to google for as it is also quite difficult to describe. The closest I can get to finding a solution is this JSFiddle:
http://jsfiddle.net/xtyus/1/
(function($){
/* Store the original positions */
var d1 = $('.one');
var d1orgtop = d1.position().top;
var d2 = $('.two');
var d2orgtop = d2.position().top;
var d3 = $('.three');
var d3orgtop = d3.position().top;
var d4 = $('.four');
var d4orgtop = d4.position().top;
/* respond to the scroll event */
$(window).scroll(function(){
/* get the current scroll position */
var st = $(window).scrollTop();
/* change classes based on section positions */
if (st >= d1orgtop) {
d1.addClass('latched');
} else {
d1.removeClass('latched');
}
if (st >= d2orgtop) {
d2.addClass('latched');
} else {
d2.removeClass('latched');
}
if (st >= d3orgtop) {
d3.addClass('latched');
} else {
d3.removeClass('latched');
}
if (st >= d4orgtop) {
d4.addClass('latched');
} else {
d4.removeClass('latched');
}
});
})(window.jQuery);
However I am not sure that is going in the right direction, this pulls images up and covers the previous image, but notice on the Seaham Hall site the images don't appear to move up at all, they are stationary and become revealed as you scroll.
How do I recreate this effect? My initial thought was to have the first image shrink as you scroll from 1000px down to 0px, and the second image grow to 1000px, and as you continue to scroll this image then shrinks and the third grows, and so on. However this means that after the first image all the other images have a starting size of 0px and there would technically be no scrolling on the page to begin with, so that is an issue.
My second thought is that perhaps the second image is fixed to the page, the first image slides up revealing the second as you scroll, the second image would not appear to move. Once the first image has gone off the top of the page the second image is detached from the page and allowed to move up with scrolling, while the third image is attached and revealed as the second moves up, this would give the exact effect seen in the Seaham website but I have no clue of it is the correct answer.
If anyone can point me to tutorials or a JSFiddle with a basic concept I can probably figure it out from there. Just stumped what direction to approach this from.
That's a nice effect. Here's one way to do it.
Put each image in a fixed position div, which takes up the entire viewport (initially) and has overflow:hidden.
Set each div's z-index to be higher than the next div's.
As the window scrolls, adjust the height of the divs as a function of the window height times the div's position (index) in the DOM, minus the window's scrollTop:
$(window).scroll(function() {
$('.D').each(function(index) {
$(this).css({
height: $(window).height()*(index+1) - $(window).scrollTop()
});
});
});
Additional content will need a higher z-index than the image divs. And note that z-index works with positioned elements only.
Fiddle
Your desired effect isn't technically a parallax background, but it's close enough that parallax jQuery frameworks should work for you.
I would suggest you research jQuery Parallax plugins as they'll likely provide the functionality you'd like without much custom work. Of course since you're dealing with large images it's also best to keep an eye on the resource management; a good plugin should be fairly efficient but others may be slow or resource intensive.
Check this jquery plugin:ScrollMagic
usage: taken from github
The basic ScrollMagic design pattern is one controller, which has several scenes attached.
Each scene has a definite start and end position and defines what happens when the container is scrolled to the specific offset.
/*
Basic workflow example
*/
// init controller
var controller = new ScrollMagic();
// assign handler "scene" and add it to controller
var scene = new ScrollScene({duration: 100})
.setPin("#my-sticky-element") // pins the element for a scroll distance of 100px
.addTo(controller); // add scene to controller
// adding multiple scenes at once
var scene2 = new ScrollScene();
var scene3;
controller.addScene([
scene2,
scene3 = new ScrollScene({duration: 200}), // add scene and assign handler "scene2"
new ScrollScene({offset: 20}) // add anonymous scene
]);
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.