I'm developing a website which involves the user being able to navigate to different parts of a page from other pages using # values in the address bar.
I have written a jQuery function to handle the scrolling here:
jQuery.fn.scrollToDiv = function(navheight)
{
if (!navheight)
{
navheight = 30;
}
var offset = this.offset();
var offsetTop = offset.top;
var totalScroll = offsetTop-navheight-27;
$('body,html').animate({
scrollTop: totalScroll
}, 500);
}
And I am calling the function in 2 different scenarios; when the user clicks a link where the object is on the current page, and when the user clicks a link that takes them to another page before scrolling to the element. See below:
When you are on the page:
$('.gotoPage').on('click', function(e)
{
e.preventDefault();
var sPath = window.location.pathname;
var sPage = sPath.substring(sPath.lastIndexOf('/') + 1);
if (sPath != '' && sPath != 'home')
{
var href = $(this).attr('href');
handleScroll(href);
}
});
and when you are not on the page:
$(document).ready(function(e)
{
var target = window.location.hash;
if (target != '')
{
$(target).scrollToDiv(30);
}
});
It works perfectly when you are on the page and click the link, however when you're not on the page, it takes you to the subsequent page as you'd expect but it doesn't then scroll to the required element.
Let me know if you need any more information
Thanks in advance
EDIT: Added function handleScroll(target)
function handleScroll(target)
{
if (target != '')
{
$(target).scrollToDiv(30);
}
}
Following your comment:
I've noticed when refreshing the page is that it scrolls down then
jumps back to the top of the page
It seems that your script does work, but something affecting it afterwards.
I believe that there are some resources as additional css codes or images that aren't being taken in account when the scroll animation takes effect and since that function works by top offset - you must be sure that you're using it after all the resources that might affect the document's height or element's offset, are being loaded.
Therefore, instead of using your script in .ready(), use .load().
.ready() vs. .load()
In most cases, the script can be run as soon as the DOM hierarchy has
been fully constructed. The handler passed to .ready() is guaranteed
to be executed after the DOM is ready, so this is usually the best
place to attach all other event handlers and run other jQuery code.
In cases where code relies on loaded assets (for example, if the
dimensions of an image are required), the code should be placed in a
handler for the load event instead.
Related
Consider the following snippet:
<div id="help"></div>
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
<script>
var loadPage = function (){
$("#help").load("http://localhost:3000/manual.html");
}
onload=loadPage;
</script>
This exists on my main page:
http://localhost:3000/
The above code works fine and loads my manual page. But if I click a link like this in manual.html:
<a href='#introduction'>Introduction</a>
Then the page in the help div jumps to the #introduction section, however the url in my browser updates to:
http://localhost:3000/#introduction
This is pointless because the #introduction anchor only exists in manual.html, how can I prevent the links in the #help div from affecting the address bar in the browser?
Try this
$('a').click(function(e) {
e.preventDefault();
$("#help").load($(this).attr('href'));
})
By using offset and preventDefault
$('a').click(function(e) {
// Go to '#introduction'
var targetId = $(this).attr('href');
$('html, body').offset({ top: $(targetId).offset().top, left: 0 });
// this prevent 'http://localhost:3000/#introduction'
e.preventDefault();
});
See this post
I already had a function that could scroll the #help window to a heading, when you click on objects in the parent it scrolls to the relevant section in the help window:
var moveTo = function(destination){
//position of the top of the help window - does not change.
var helpWindow = $("#help").offset().top;
//difference between current scroll position and target position.
var relativeDistance = $(destination).position().top - helpWindow;
//add the distance from current position to the top to get distance to target from top
var absoluteDistance = relativeDistance+ $("#help").scrollTop();
$("#help").animate({
scrollTop: absoluteDistance
}, 1000);
}
Using e.preventDefault() I was able to use this function to do what I want.
For others heading down this path there are two other small things to consider.
Firstly, make sure you nest the .click() function inside the callback from page load, as the hyper links won't exist until the page is loaded. Secondly, You will probably want to use a child selector eg $('#help a').click() to ensure you are only altering the behaviour on links inside the child.
$("#help").load("http://localhost:3000/manual.html", function(){
$('#help a').click(function(e) {
e.preventDefault(); //suppress standard link functionality
moveTo($(this).attr('href')); //scroll to link instead.
})
});
I have a page which is generated using JavaScript. I want to store the $(window).scrollTop() value in a hash (like #position=xxx) so when the page is refreshed and the items are generated again, the user can continue scrolling from where they left off.
I'm trying to modify the hash on beforeunload, which does not seem to work. The first part of the function below tries to change the hash, and the second part is taking care of the modification of the scrollTop on page load. The second part works fine, I'm only stuck with the first part.
$(window).on('beforeunload',function(){
window.location.hash = "position="+$(window).scrollTop();
}).on('load hashchange',function(){
var scrollregex = /^position=(\d+)$/,
hash = window.location.hash.substring(1);
if (scrollregex.test(hash)) $(window).scrollTop(parseInt(hash.match(scrollregex)[1]));
});
Is there a different event for this, or otherwise how can I change the hash when the user triggers a refresh?
I ended up ditching the hash idea and going with a sessionStorage based approach. I wrote my own wrapper for session/local storage which I use in the following example.
$(window).on('beforeunload',function(){
var scrltop = $(window).scrollTop();
if (scrltop > 0) SStorage.set('position',scrltop);
else SStorage.del('position');
}).on('load',function(){
if (SStorage.has('position')){
var pos = parseInt(SStorage.get('position'));
if (!isNaN(pos) && $(window).scrollTop() === 0) $(window).scrollTop(pos);
}
});
Chrome doesn't seem to allow you to update the hash in the onbeforeunload event. I had a similar issue. I moved my code to the unload event, which worked but introduced other issues. To solve them I have to force a reload, note reload will not work without the setTimeout.
$(window).on('unload', function(){
/* Do hash change here */
setTimeout(function () { window.location.reload(true); }, 0);
});
I needed to be able to load a particular page in an iframe on demand, so I used a simple wrapper:
function updateFrame(url) {
frames[0].location = url;
}
Then I was asked to load the page to a particular point, which was non-trivial, since the pages were not within my control and there weren't always <a name> anchors to rely on. So some poking around showed that IDs could be used as anchors.
That is to say, you can scroll to <div id = "somewhere-down-the-line"> with:
updateFrame("http://host/page#somewhere-down-the-line");
except this call also scrolls the entire viewport up so that the above <div> goes to the top and everything in the parent page above it scrolls out of view.
How do I modify updateFrame(url) so that it scrolls the page within the <iframe> but leaves the rest of the page as it is?
This hack worked for me on Firefox 20.0.1/Windows. Essentially, I load the page first, then jump to the target:
function updateFrame(url) {
if (url.indexOf('#') > -1) {
mainPage = url.split('#')[0];
frames[0].location = mainPage;
}
frames[0].location = url;
}
I would like to be able to use this in other browsers as well. I have been trying to get it to work in Chrome. Maybe I'll even try Internet Explorer...
If a hack is ok, and what you're looking for is cross-browser try using scrollTop to reset where you were.
E.g. if it is the body that scrolls
function updateFrame(url) {
//save where you were
var oldScroll = document.body.scrollTop;
//this moves our body!
frames[0].location = url;
//move it back
document.body.scrollTop = oldScroll;
}
Of course if it doesn't actually scrolls the entire viewport and instead modifies a parent div or something, the scrollTop property will be on that element too.
Let me know if this works, but screws up the scrolling on the frame, because I can modify this to account for a difference between the two scrollTops
You could try turning the bolts yourself by detecting the height of the element you want, and forcing the scrollTop of the frame.
function updateFrame(url) {
//get the parts
var parts = url.split('#');
//go to the url
frames[0].location = parts[0];
//if there was an anchor
var anchor;
if (parts.length > 0 && parts[1].length > 0) {
//may want to account for a[name="PARTS[1]"] too
anchor = frames[0].document.getElementById(parts[1]);
//set the scroll of it yourself, using some sort of library to get "fullTop"
frames[0].document.body.scrollTop = anchor.fullTop();
}
}
Where "fullTop" is equivalent to the distance between the top of the iframe, and the element.
Like jQuery's .offset() or YUI's getXY(el).[1]
What worked for me on Firefox 20.0.1/Windows. Essentially, I load the page first, then jump to the target:
function updateFrame(url) {
if (url.indexOf('#') > -1) {
mainPage = url.split('#')[0];
frames[0].location = mainPage;
}
frames[0].location = url;
}
On Chrome 28.0/Windows, calling updateFrame(url) followed by setting document.body.scrollTop = 0 (thanks to this answer) had the desired effect, though only in the console. I am still testing on other browsers; a more elegant solution is always appreciated :)
As mentioned in the question, though, I would like to be able to use this in other browsers as well. Maybe I'll even try Internet Explorer...
I tried with this code but, didnĀ“t worked.
<a href="http://altodesign.pt/#portfolio" onClick="loadintoIframe('myframe,'portfolio/mmteam.html');">
you can try something like this
a href="javavcipt:document.getElementById('myframe').src = 'portfolio/mmteam.html';"
I would never use javascript ...
I have had a look into your webpage (plenty to learn, like add scripts to the end of the page, create a global javascript object to hold all website actions, etc ... but that's not the question)
I could see that, even thought you jump to #CONTACTOS you are not making the use of the hash at all... and you should!
using the hash would let you do things like:
http://altodesign.pt/#portfolio-cooptaxis
and that would jump to portfolio anchor and load the cooptaxis.html into the iframe and you stoped using javascript:loadintoIframe('myframe', 'portfolio/mmteam.html') at all, as that will cause Google Analytics and Crawlers not to follow up your links for example ...
your method could be something simple like
$(function() {
// let's see if we have an hash on the page
var hash = document.location.hash;
if(hash.length > 0) {
if(hash.instr('-') >= 0) {
// supposing will have only one char '-'
var img = hash.split('-')[1];
// let's remove the frame info from the hash
hash = hash.split('-')[0];
// there's a call to load into the iframe, let's load it
$("#myframe").attr("src", "portfolio/" + img + ".html")
}
// let's fly
jumpTo(hash);
}
// let's disable the anchor links by default and use the hash
$("a[href^=#]").click(function() {
// for all links that start with the hash, let's...
document.location.hash = $(this).attr("href");
return false;
});
$(window).bind('hashchange', function() {
// everytime the hash changes let's fly
jumpTo(document.location.hash);
});
});
function jumpTo(anchor) {
var a = $("a[name='" + anchor.replace('#','') + "']"),
pos = 0;
if(a.length > 0) {
// we have found the anchor, let's grab it's top position
pos = a.position().top;
}
// if we got here and pos === 0, we did not found the anchor
// for the given hash... maybe the user is playing around ...
// and we shall fly
$('body,html').animate({
scrollTop: pos
}, 800);
}
justthis will allow you to avoid using javascript to jump your links, as all they now have to have is simple: Portfolio
Let we say that you have page1.html in-which a link to page2.html you want it to be opened in an iframe in page1.html
in page1.html
link
<iframe name="iframe-name"></iframe>
Then you are able to add any anchor you want. It is just a matter of naming your iframe and then targeting it in the link!
I am currently using jQuery-Smooth-Scroll to smoothly scroll up and down to various anchor positions on one of my pages (Page 1). However, what I would also like to be able to do is, from another page (Page 2), link to Page1 (appending #bookmark to the url) and have jQuery-Smooth-Scroll pick up on the fact I am calling the page with a #bookmark and have it smoothly scroll down to the relevant position once the page has completed loading. I don't know if this is a possibility or not?
This is the version of Smooth-Scroll that I'm using:
https://github.com/kswedberg/jquery-smooth-scroll
I'm still relatively new to jQuery so I may be overlooking something obvious.
Ajma's answer should be sufficient, but for completeness:
alert(location.hash)
Edit: a more complete example:
// on document.ready {
if (location.hash != '') {
var a = $("a[name=" + location.hash.substring(1) + "]");
// note that according to w3c specs, the url hash can also refer to the id
// of an element. if so, the above statement becomes
// var a = $(location.hash);
if (a.length) {
$('html,body').animate({
scrollTop: $(a).offset().top
}, 'slow');
}
}
// }
It's possible, you want to put a call into the smooth scroll function when the page is finished loading. in jQuery, it's using $(document).ready(function () { your code } );
You'll need to put something in to parse your url to extract the #bookmark and then call the smooth scroll.