fast jump to make javascript object visible on screen - javascript

I have a function that scrolls to make an object visible on screen. However, it is slow. Is there any way to jump to without scrolling the entire page ?
If there is a reasonably portable method that does not use jquery, I would prefer a native one. My definition of reasonably portable is that it works on fairly recent chrome and firefox.
function scrollToId(id) { //TODO: Fix Jquery madness below
var element = $('#' + id); //document.getElementById(id);
var offset = element.offset().top;
if (offset > window.innerHeight) {
// Not in view so scroll to it
$('html,body').animate({
scrollTop: offset
}, 10);
return false;
}
return true;
}

Your best bet for jumping to an area quickly on the page is to use the window.scroll(x, y) method. This does not animate the page, however, it will get your user to that area on the page very quickly.
function scrollToId(id) { //TODO: Fix Jquery madness below
var element = $('#' + id); //document.getElementById(id);
var yPos = element.offset().top;
var xPos = 0; //This could be the left position of an element like element.offset().left
if (offset > window.innerHeight) {
// Not in view so scroll to it
window.scroll(xPos, yPos);
return false;
}
return true;
}
http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_win_scrollto2
https://developer.mozilla.org/en-US/docs/Web/API/Window/scroll

Is there any way to jump to without scrolling the entire page ?
Try utilizing <a> element with #id set at href attribute , where #id is id of DOM element
#abc {
position:relative;
top:350px;
color:green;
}
#def {
position:relative;
top:650px;
color:orange;
}
abc def
<div id="abc">abc</div>
<div id="def">def</div>

I am not sure how much of a difference this would make, but try snippet below. Instead of scrolling it, just go to it.
function scrollToId(id)
{
var element = $('#' + id);
var curPos = element.offset();
var curTop = curPos.top;
var screenHeight = $(window).height();
return (curTop > screenHeight) ? false : true;
}

Related

Scroll bottom in JavaScript

I have a working bottom function in JavaScript to detect if the user scrolls at the bottom. However, a problem comes when the user has a strange resolution (like windows scale) or when you zoom. The function is not working anymore and can't detect the bottom.
Here is what I did :
const bottom = e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
if (bottom) {
this.props.getNewValues();
}
Is there a way to avoid that? Even when you don't zoom, this is not working for people displaying the site on a TV or something like this (like a friend of mine did)
Thanks you
EDIT : I'm applying this on a precise element and I repeat that my solution is working except by unzooming. Unzooming provides float values that made the response not really accurate (it goes from 1 to 50px of difference based on the zoom made)
I use this function (can't take credit as someone else wrote it - sorry for no credit - it was ages ago). Maybe you can adapt this to your use case:
(function($) {
//CHECK SCROLLED INTO VIEW UTIL
function Utils() {
}
Utils.prototype = {
constructor: Utils,
isElementInView: function (element, fullyInView) {
var pageTop = $(window).scrollTop();
var pageBottom = pageTop + $(window).height();
var elementTop = $(element).offset().top;
var elementBottom = elementTop + $(element).height();
if (fullyInView === true) {
return ((pageTop < elementTop) && (pageBottom > elementBottom));
} else {
return ((elementTop <= pageBottom) && (elementBottom >= pageTop));
}
}
};
var Utils = new Utils();
//END CHECK SCROLLED INTO VIEW UTIL
//USING THE ELEMENT IN VIEW UTIL
//this function tells what to do do when the element is or isnt in view.
//var inView = Utils.isElementInView(el, false); Where FALSE means the element doesnt need to be completely in view / TRUE would mean the element needs to be completely in view
function IsEInView(el) {
var inView = Utils.isElementInView(el, false);
if(inView) {
//console.log('in view');
} else {
//console.log('not in view');
}
};
//Check to make sure the element you want to be sure is visible is present on the page
var variableOfYourElement = $('#variableOfYourElement');
//if it is on this page run the function that checks to see if it is partially or fully in view
if( variableOfYourElement.length ) {
//run function on page load
IsEInView(variableOfYourElement);
//run function if the element scrolls into view
$(window).scroll(function(){
IsEInView(variableOfYourElement);
});
}
//END USING THE ELEMENT IN VIEW UTIL
})(jQuery);

Execute something while element is in view

I am using the Jquery inview plugin and I am trying to load some elements whenever the user reached the footer of the page. While doing this, I discovered a bug where if the user holds the scroll-click and drags the mouse towards the bottom, in some cases the elements will not load anymore until the footer is out of the view and then back into the view.
Here is the function that I have so far to load the elements when the footer is in the viewport:
//Infinite load function. Uses jquery.inview
$scope.addMoreElements = function(){
$scope.limitElementsPerPage += 16;
$('.footer').on('inview', function(event, isInView) {
if (isInView) {
// element is now visible in the viewport
$scope.limitElementsPerPage += 16;
} else {
// element has gone out of viewport
//do nothing
}
});
};
I am using Angularjs as well as jQuery for this project. Essentially, what I think I need is something that checks at about 1-2 seconds if the element is still in view. I am not exactly sure I should do this at the moment. This is what I tried to do to solve this issue:
$scope.$watch($('.footer'), function(){
$('.footer').on('inview', function(event, isInView) {
setTimeout(function(){
while(isInView){
console.log('test')
}
}, 1000);
});
});
This unfortunately, will crash the browser (I am not sure how I would go about doing this with the setTimeout or the other related functions).
Any help or ideas on how to do this would be greatly appreciated.
Thank you in advance.
InView adds a new event for elements, that triggers when the element enters the viewport. Probably some times you just have the footer in the viewport at all times, so that is why it fails.
I think you need to redesign the logic of the page to use the 'scroll' event on whatever element contains the added items and scrolls for the infinite view and in that event to check if the footer is in the viewport, not if it enters.
Personally I use this extension for checking if it is in the viewport:
(function($) {
$.inviewport = function(element, settings) {
var wh=$(window).height();
var wst=$(window).scrollTop();
var et=$(element).offset().top;
var eh=$(element).height();
return !(wh + wst <= et)&&!(wst >= et + eh);
};
$.extend($.expr[':'], {
"in-viewport": function(a, i, m) {
return $.inviewport(a);
}
});
})(jQuery);
Here are couple of functions you can use:
var getScrollY = function(){
var supportPageOffset = window.pageXOffset !== undefined;
var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ?
document.documentElement.scrollTop : document.body.scrollTop;
return y;
}
function get_elem_y( elem ) {
var box = elem.getBoundingClientRect();
return box.top + getScrollY();
}
And then you can listen to the scroll event, assume footer is something like <div id="footer">...</div>
var footer = document.getElementById("footer"); // get footer
var b_foot_visible = false;
window.addEventListener("scroll", function() {
var y = get_elem_y(footer);
var pageHeight = ( window.innerHeight || document.body.clientHeight);
if((getScrollY() + pageHeight) > y ) {
// footer is visible
if(!b_foot_visible) {
// TODO: add something
b_foot_visible = true;
}
} else {
// footer is not visible
if(b_foot_visible) {
// TODO: remove something
b_foot_visible = false;
}
}
});
Thus, when the scrollY + pages height reaches the footer elements Y coordinate you can do something to display things for the footer.
You might also add check in the beginning to test if the footer is already visible.

Change url when manually scrolled to an anchor?

By Default, if I have anchors in my website, then the URL on the address bar is changed, when I click on a link (ie. www.mysite.com/#anchor)
Is it possible to change the URL in the address bar instantly when I scroll to an anchor? Or have a long document with multiple anchors and the url changes on address bar, when I hit a new anchor?
Try using this jquery plugin: Scrollorama. It has tons of cool features and you can use window.location.hash to update your browsers hash.
Alternatively, you can add a "scroll" event to check when an anchor is reached.
Here is a working fiddle to illustrate the event: http://jsfiddle.net/gugahoi/2ZjWP/8/
Example:
$(function () {
var currentHash = "#initial_hash"
$(document).scroll(function () {
$('.anchor_tags').each(function () {
var top = window.pageYOffset;
var distance = top - $(this).offset().top;
var hash = $(this).attr('href');
// 30 is an arbitrary padding choice,
// if you want a precise check then use distance===0
if (distance < 30 && distance > -30 && currentHash != hash) {
window.location.hash = (hash);
currentHash = hash;
}
});
});
});
you can use HTML 5 pushstate to change the URL in the address bar
window.history.pushstate
https://developer.mozilla.org/en-US/docs/DOM/Manipulating_the_browser_history
How can I use window.history.pushState 'safely'
Bind a handler to jquery scroll event.
Check if an anchor is currently visible on-screen with this jquery
script.
Use pushstate or set location (probably will cause jumps)
You can bind to the jQuery scroll event (http://api.jquery.com/scroll/) and on each call of the callback called, check how far on the document the user has scrolled by checking this value: .scrollTop (http://api.jquery.com/scrollTop/) and set the anchor by manipulating te location.hash object (http://www.w3schools.com/jsref/prop_loc_hash.asp).
It would be something like this:
// Checks if the passed element is visible on the screen after scrolling
// source: http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
function isScrolledIntoView(elem) {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
$('#document').scroll(function(e) {
var anchors = $('.anchor');
for (var i = 0; i < anchors.length; ++i) {
if (isScrolledIntoView(anchors[i])){
var href = $(anchors[i]).attr('href');
location.hash = href.slice(href.indexOf('#') + 1);
break;
}
}
});
You could be more precise, if you sort the anchors after selecting them, so that the first visible anchor will be set always.
Plain js version
While researching how to update the URL based off positions of HTML section elements on the screen, I kept finding this thread so I hope this is a good place to post this.
This function loops over the HTML section elements.
function updateFragId() {
var len = sections.length;
for (var i = 0; i < len; i++) {
var id = sections[i].id;
Collects the Y scroll position relative to the viewport.
var rect = sections[i].getBoundingClientRect().y;
convert the two arrays into an object
var pageData = {id:id, rect:rect};
set a range for the code to trigger in between. Here it will trigger when the top of the section element is between -200px to 400px
if (pageData.rect > -200 && pageData.rect < 400) {
only run once by making sure the pageData.id and location.hash dont already match. This stops it from flooding your browser with events.
if (pageData.rect > -100 && pageData.rect < 100) {
if (pageData.id !== location.hash.substr(1)) {
fragmentId = pageData.id;
setActiveLink(fragmentId);
} else {
return;
}
}
}
}
window.addEventListener('scroll', updateFragId);
I use a debouncer on this block of code with another block to set the active link. But this is just how to track the # anchors.
I think you need to do something like this. Not tried in action
var myElements = $("div.anchor"); // You target anchors
$(window).scroll(function(e) {
var scrollTop = $(window).scrollTop();
myElements.each(function(el,i) {
if ($(this).offset().top > scrollTop && $(myElements[i+1]).offset().top < scrollTop) {
location.hash = this.id;
}
});
});

jQuery scroll to div on hover and return to first element

I basically have a div with set dimensions and overflow: hidden. That div contains 7 child divs (but only shows one at a time) that I would like to be smoothly scrolled through vertically when their respective links are hovered.
However, the first section (div) doesn't have a link and is the default section when no link is hovered.
Take a look at this jsFiddle to see a basic structure of what I'm talking about: http://jsfiddle.net/YWnzc/
I've attempted to accomplish this with jQuery scrollTo but haven't been able to get it to work.
Any help would be greatly appreciated. Thanks.
Something like this?
http://jsfiddle.net/YWnzc/5/
code:
jQuery("#nav").delegate("a", "mouseenter mouseleave", function (e) {
var i, self = this,
pos;
if (e.type == "mouseleave") {
i = 0;
}
else {
//Find out the index of the a that was hovered
jQuery("#nav a").each(function (index) {
if (self === this) {
i = index + 1; //the scrollTop is just calculated from this by a multiplier, so increment
return false;
}
});
}
//Find out if the index is a valid number, could be left undefined
if (i >= 0) {
//stop the previous animation, otherwise it will be queued
jQuery("#wrapper").stop().animate({
scrollTop: i * 200
}, 500);
//I would retrieve .offsetTop, but it was reporting false values :/
}
e.preventDefault();
});
FYI : That JSFIDDLE you sent me to went to MooTools framework, not jQuery... fyi. (might be why its not working?
Copy and paste this code exactly and it will work in jQuery for animated scrolling.
Try this for smooth scrolling within the DIV, I tested it - it works great. You
$(function() {
function filterPath(string) {
return string
.replace(/^\//,'')
.replace(/(index|default).[a-zA-Z]{3,4}$/,'')
.replace(/\/$/,'');
}
var locationPath = filterPath(location.pathname);
var scrollElem = scrollableElement('#wrapper');
// Any links with hash tags in them (can't do ^= because of fully qualified URL potential)
$('a[href*=#]').each(function() {
// Ensure it's a same-page link
var thisPath = filterPath(this.pathname) || locationPath;
if ( locationPath == thisPath
&& (location.hostname == this.hostname || !this.hostname)
&& this.hash.replace(/#/,'') ) {
// Ensure target exists
var $target = $(this.hash), target = this.hash;
if (target) {
// Find location of target
var targetOffset = $target.offset().top;
$(this).click(function(event) {
// Prevent jump-down
event.preventDefault();
// Animate to target
$(scrollElem).animate({scrollTop: targetOffset}, 400, function() {
// Set hash in URL after animation successful
location.hash = target;
});
});
}
}
});
// Use the first element that is "scrollable" (cross-browser fix?)
function scrollableElement(els) {
for (var i = 0, argLength = arguments.length; i <argLength; i++) {
var el = arguments[i],
$scrollElement = $(el);
if ($scrollElement.scrollTop()> 0) {
return el;
} else {
$scrollElement.scrollTop(1);
var isScrollable = $scrollElement.scrollTop()> 0;
$scrollElement.scrollTop(0);
if (isScrollable) {
return el;
}
}
}
return [];
}
});
FYI : Credit for this code does not go to me as an individual developer, although I did slightly tweak the code. The owner and creator of this code is Chris Coyier and you can find more about this scrolling code here:
http://css-tricks.com/snippets/jquery/smooth-scrolling/
Here's a working example: http://jsfiddle.net/YWnzc/7/
And the code (pretty similar to rizzle's, with a couple changes that I'll explain):
$('a').hover(function(){
var selector = $(this).data('section');
var scrollAmount = $(selector).offset().top + $('#wrapper')[0].scrollTop - 129;
$('#wrapper').animate({scrollTop: scrollAmount}, 250);
},function(){
$('#wrapper').animate({scrollTop: 0}, 250);
});
First, var selector = $(this).data('section'); because in jsFiddle, the href attribute was returning the full path of the page + the hash. So I changed it to an html5 data attribute (data-section).
The next line is similar to rizzle's, except that we grab the offset of the section and add it to the current scrollTop value of the #wrapper. As he pointed out, there are some weird offset issues going on still, and I found that subtracting 129 did the trick. While this 129 number might seem like something that is likely to break, I did test out changing the sizes of the sections, making them not equal, etc, and it continued to work. I'm using Chrome, and perhaps a non-webkit browser would need a different constant to subtract. But it does seem like that 129 number is at least some kind of constant.
The rest should be pretty self-explanatory.
One thing to note: as you move your cursor over the <a> tags, the content of the #wrapper div will seem to jump around, but that's just because the mouseleave part of the hover event briefly gets triggered as the cursor moves. I'm sure you can solve that one though :)
$("#nav a").hover(function () {
var sectionName = $(this).attr("href");
var sectionPos = $(sectionName).offset().top;
var wrapperPos = $("#wrapper").offset().top;
var wrapperScroll = $("#wrapper").scrollTop();
var scrollPos = sectionPos - wrapperPos + wrapperScroll;
$("#wrapper").stop().animate({scrollTop:scrollPos}, 600);
}, function () { $("#wrapper").stop().animate({scrollTop:0}, 600); });

infinite-scroll jquery plugin

I am trying to set up infinite-scroll on a site I am developing with Coldfusion, I am new to javascript and jquery so I am having some issues wrapping my head around all of this. Do I need to have pagination on my site in order to use the infinite-scroll plugin, or is there a way to do it with out it?
You do not need infinite scroll plug-in for this. To detect when scroll reaches end of page, with jQuery you can do
$(window).scroll(function () {
if ($(window).scrollTop() >= $(document).height() - $(window).height() - 10) {
//Add something at the end of the page
}
});
Demo on JsFiddle
I'm using Hussein's answer with AJAX requests. I modified the code to trigger at 300px instead of 10px, but it started causing my appends to multiply before the AJAX request was finished since the scroll call triggers much more frequently in a 300px range than a 10px range.
To fix this, I added a trigger that would be flipped on successful AJAX load. My code looks more like this:
var scrollLoad = true;
$(window).scroll(function () {
if (scrollLoad && $(window).scrollTop() >= $(document).height() - $(window).height() - 300) {
scrollLoad = false;
//Add something at the end of the page
}
});
then in my AJAX response, I set scrollLoad to true.
I built on top of Hussein's little example here to make a jQuery widget. It supports localStorage to temporarily save appended results and it has pause functionality to stop the appending every so often, requiring a click to continue.
Give it a try:
http://www.hawkee.com/snippet/9445/
$(function(){
$(window).scroll(function(){
if($(document).height()<=$(window).scrollTop()+$(window).height()+100){
alert('end of page');
}
});
});
Some one asked for explanation so here is the explanation
here $(document).height()-->is the height of the entire document.In most cases, this is equal to the element of the current document.
$(window).height()-->is the height of the window (browser) means height of whatever you are seeing on browser.
$(window).scrollTop()-->The Element.scrollTop property gets or sets the number of pixels that the content of an element is scrolled upward. An element's scrollTop is a measurement of the distance of an element's top to its topmost visible content. When an element content does not generate a vertical scrollbar, then its scrollTop value defaults to 0.
$(document).height()<=$(window).scrollTop()+$(window).height()+100
add $(window).scrollTop() with $(window).height() now check whether the result is equal to your documnet height or not. if it is equal means you reached at the end.we are adding 100 too because i want to check before the 100 pixels from the bottom of document(note <= in condition)
please correct me if i am wrong
I had same problem but didn't find suitable plugin for my need. so I wrote following code. this code appends template to element by getting data with ajax and pagination.
for detecting when user scrolls to bottom of div I used this condition:
var t = $("#infiniteContent").offset().top;
var h = $("#infiniteContent").height();
var ws = $(window).scrollTop();
var dh = $(document).height();
var wh = $(window).height();
if (dh - (wh + ws) < dh - (h + t)) {
//now you are at bottom of #infiniteContent element
}
$(document).ready(function(){
$.getJSON("https://jsonplaceholder.typicode.com/comments", { _page: 1, _limit:3 }, function (jsonre) {
appendTemplate(jsonre,1);
});
});
function appendTemplate(jsonre, pageNumber){
//instead of this code you can use a templating plugin like "Mustache"
for(var i =0; i<jsonre.length; i++){
$("#infiniteContent").append("<div class='item'><h2>"+jsonre[i].name+"</h2><p>"+jsonre[i].body+"</p></div>");
}
if (jsonre.length) {
$("#infiniteContent").attr("data-page", parseInt(pageNumber)+1);
$(window).on("scroll", initScroll);
//scroll event will not trigger if window size is greater than or equal to document size
var dh = $(document).height() , wh = $(window).height();
if(wh>=dh){
initScroll();
}
}
else {
$("#infiniteContent").attr("data-page", "");
}
}
function initScroll() {
var t = $("#infiniteContent").offset().top;
var h = $("#infiniteContent").height();
var ws = $(window).scrollTop();
var dh = $(document).height();
var wh = $(window).height();
if (dh - (wh + ws) < dh - (h + t)) {
$(window).off('scroll');
var p = $("#infiniteContent").attr("data-page");
if (p) {
$.getJSON("https://jsonplaceholder.typicode.com/comments", { _page: p, _limit:3 }, function (jsonre) {
appendTemplate(jsonre, p);
});
}
}
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div id="infiniteContent"></div>
If you have a scrollable element, like a div with scroll overflow, but no scrollable document/page, you can take this way.
$(function () {
var s = $(".your-scrollable-element");
var list = $("#your-table-list");
/* On element scroll */
s.scroll(function () {
/* The scroll top plus element height equals to table height */
if ((s.scrollTop() + s.height()) == list.height()) {
/* you code */
}
});
});
I wrote this function using Hussein and Nick's ideas, but I wanted it to use promises for the callback. I also wanted the infinite scrolling area to be on a fixed div and not just the window if the div is sent into the options object. There is an example of that in my second link below. I suggest using a promise library like Q if you want to support older browsers. The cb method may or may not be a promise and it will work regardless.
It is used like so:
html
<div id="feed"></div>
js
var infScroll = infiniteScroll({
cb: function () {
return doSomethingPossiblyAnAJAXPromise();
}
});
If you want the feed to temporarily stop you can return false in the cb method. Useful if you have hit the end of the feed. It can be be started again by calling the infiniteScroll's returned object method 'setShouldLoad' and passing in true and example to go along with the above code.
infScroll.setShouldLoad(true);
The function for infinite scrolling is this
function infiniteScroll (options) {
// these options can be overwritten by the sent in options
var defaultOptions = {
binder: $(window), // parent scrollable element
loadSpot: 300, //
feedContainer: $("#feed"), // container
cb: function () { },
}
options = $.extend(defaultOptions, options);
options.shouldLoad = true;
var returnedOptions = {
setShouldLoad: function (bool) { options.shouldLoad = bool; if(bool) { scrollHandler(); } },
};
function scrollHandler () {
var scrollTop = options.binder.scrollTop();
var height = options.binder[0].innerHeight || options.binder.height();
if (options.shouldLoad && scrollTop >= (options.binder[0].scrollHeight || $(document).height()) - height - options.loadSpot) {
options.shouldLoad = false;
if(typeof options.cb === "function") {
new Promise(function (resolve) {resolve();}).then(function() { return options.cb(); }).then(function (isNotFinished) {
if(typeof isNotFinished === "boolean") {
options.shouldLoad = isNotFinished;
}
});
}
}
}
options.binder.scroll(scrollHandler);
scrollHandler();
return returnedOptions;
}
1 feed example with window as scroller
2 feed example with feed as scroller

Categories

Resources