Change url when manually scrolled to an anchor? - javascript

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;
}
});
});

Related

fast jump to make javascript object visible on screen

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;
}

Cannot get onScroll function to fire only once

I'm pretty new to JavaScript and found myself a little stuck on one seemingly super simple task with an onScroll function.
I have the following code (see below) which is working great. However, it currently fires every time the user scrolls up or down, returning to that particular vertical location on the page. I only want it to fire the first time it is scrolled to after a page load.
Please help!
Big respect and many thanks in advance!
$(window).scroll(function () {
$('#pro1').each(function () {
var topOfWindow = $(window).scrollTop(),
bottomOfWindow = topOfWindow + $(window).height();
var imagePos = $(this).offset().top;
if(imagePos < bottomOfWindow-200 && imagePos >= topOfWindow-500){
$(this).addClass('bigEntrance2');
}else{
$(this).removeClass('bigEntrance2');
}
});
});
You can use the one method. It is the same as the on method, but only fires once per page load. So for your example, it would look like this:
$(window).one("scroll", function()
{
# Scroll code here
});
Edit: I think I understand your question better. You want the code to check when you're in that scroll region, but only execute once. So to do that, you first should designate a custom event:
$(window).one("customEvent", function()
{
$(this).addClass('bigEntrance2');
});
And then trigger that event when you want:
if(imagePos < bottomOfWindow-200 && imagePos >= topOfWindow-500)
{
$(this).trigger("customEvent");
}
That way, the class 'bigEntrance2' is only applied once, which is what I think you want.
Well you can use a variable and use it as a flag. The first time the user scrolls once the page loads, you set it to true. After that, you check that variable to avoid doing always the same code.
var alreadyScrolled = false;
$(window).scroll(function () {
if(!alreadyScrolled) {
$('#pro1').each(function () {
var topOfWindow = $(window).scrollTop(),
bottomOfWindow = topOfWindow + $(window).height();
var imagePos = $(this).offset().top;
if(imagePos < bottomOfWindow-200 && imagePos >= topOfWindow-500){
$(this).addClass('bigEntrance2');
}else{
$(this).removeClass('bigEntrance2');
}
});
alreadyScrolled = true;
}
});

Menu fixed on single page - change color

i want to create a menu for a single page website with link to div from page.
The menu look like this:
<li>Home</li>
...
<div id="home-link"></div>
I want to change color of link from menu when i am in area of home-link div. How is possible to make that thing?
Thanks for answers and for your help.
Have a nice day.
You will need JavaScript for this if the link element is not a child of #home-link.
Something like this:
$('#home-link').on('hover', function () {
$('li a').css('color', '#bada55');
});
This assumes you are using jQuery, but similar approach with other frameworks would work as well.
If I assume from the question that if you are hovering on #home-link and color of anchor should change, then
$('#home-link').hover(function () {
$('li a').css('color', 'red');
});
or if I assume that if the id is present in your page and you want to change the color of the anchor then
if($('#home-link').length){
$('li a').css('color', 'red');
}
I found myself with (what I believe is) the same question: how can I change which navigation link is highlighted to reflect the area I've scrolled to, whether I get there by using the link or just by scrolling down the page?
Here's a page with a very helpful tutorial.
The theory:
We create an array of all our nav a href’s. We then use some calculations using the scroll function. We find the section id, calculate it’s height, see if it’s greater or less than the value from the window top, and if the window falls in between, we add a class nav-active to the list item in question. We create a conditional also, because if the top of a section is not reached and the page can’t scroll anymore, we want to highlight this section.
And the relevant jQuery code:
/**
* This part handles the highlighting functionality.
* We use the scroll functionality again, some array creation and
* manipulation, class adding and class removing, and conditional testing
*/
var aChildren = $("nav li").children(); // find the a children of the list items
var aArray = []; // create the empty aArray
for (var i=0; i < aChildren.length; i++) {
var aChild = aChildren[i];
var ahref = $(aChild).attr('href');
aArray.push(ahref);
} // this for loop fills the aArray with attribute href values
$(window).scroll(function(){
var windowPos = $(window).scrollTop(); // get the offset of the window from the top of page
var windowHeight = $(window).height(); // get the height of the window
var docHeight = $(document).height();
for (var i=0; i < aArray.length; i++) {
var theID = aArray[i];
var divPos = $(theID).offset().top; // get the offset of the div from the top of page
var divHeight = $(theID).height(); // get the height of the div in question
if (windowPos >= divPos && windowPos < (divPos + divHeight)) {
$("a[href='" + theID + "']").addClass("nav-active");
} else {
$("a[href='" + theID + "']").removeClass("nav-active");
}
}
if(windowPos + windowHeight == docHeight) {
if (!$("nav li:last-child a").hasClass("nav-active")) {
var navActiveCurrent = $(".nav-active").attr("href");
$("a[href='" + navActiveCurrent + "']").removeClass("nav-active");
$("nav li:last-child a").addClass("nav-active");
}
}
});

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