I have a web page with a top link at the bottom, and anchor for it at the top of the page. I have the following JavaScript to make an element (#primary_nav) fixed after the page has been scrolled past that element:
window.onscroll = scroll;
var scrollFlag = 0;
function scroll() {
if (window.pageYOffset >= 120 && scrollFlag == 0)
{
document.getElementById('content').style.margin = '35px auto 0 auto';
document.getElementById('primary_nav').style.position = 'fixed';
document.getElementById('primary_nav').style.top = '35px';
document.getElementById('primary_nav').style.width = '100%';
scrollFlag = 1;
}
else if(window.pageYOffset < 120 && scrollFlag == 1)
{
document.getElementById('content').style.margin = '0 auto';
document.getElementById('primary_nav').style.position = 'static';
document.getElementById('primary_nav').style.top = 'auto';
document.getElementById('primary_nav').style.width = '1024px';
scrollFlag = 0;
}
}
If I disable this javascript, the link works fine and sends the user back to the top of the page. The anchor is not inside primary_nav or content.
The HTML elements are declared as follows:
<a name='top'></a>
<a href='#top' id='backtotop' title='{$this->lang->words['go_to_top']}'><img src='{$this->settings['img_url']}/top.png' alt='' /></a>
How can I have both the effect of the javascript (sticky navigation) and working anchor links?
You can also check the live site.
So far I've tried
Moving the top anchor all over <body>
Replacing the "Go to top" button's href with javascript (window.location = '#top' and window.scrollTo(0,0)) with both href and onclick. (Both of these work when put into console on Chrome)
This jsfiddle indicates that it's caused by my script conflicting with something else. The only other script accessing it is:
if($('backtotop')){$('backtotop').observe("click",function(e){Event.stop(e);window.scroll(0,0);});}
I figured out part of your issue.
I noticed I can't type location.hash = 'top' and have it jump to top unless I take it out of #content_jump. I assume this is because #content_jump is hidden.
It seems like there might be an event listener on all links that start with # that's causing the issue. I noticed most anchor links are doing little fancy things.
My own Javascript overwrote a function of the same name in the default scripts, which caused the failure. Renaming my scroll function fixed the issue.
Related
I have these icons above each section on my page (the largish circular icons, please see example: http://pftest.fhero.net) with colored hover states... what I would really love to do is have them change to the active hover states as the user scrolls to each section (preferably with a simple fade transition) - much like the effect of highlighting the active links/section in the navigation.
There are many tutorials, plugins and questions on this site and so forth for highlighting active sections in a navigation however, but doesn't seem to be much that I can find relating to applying the effect to another div or image on the page...
I'm definitely not any kind of jQuery expert but I'm wondering if one of the myriad of scripts/plugins available which are typically used for highlighting active states in navigation could simply be adapted to this scenario somehow to achieve the same effect? Perhaps even the one I am currently using on my page?
Here is the script I'm using for highlighting the active section in the navigation on my page:
/* Scroll Navigation highlight */
$("#work-section1").parent().addClass('active');
var main = main = $('#mainmenu ul');
$('.scroll').click(function(event) {
event.preventDefault();
var full_url = this.href,
parts = full_url.split('#'),
trgt = parts[1],
target_offset = $('#'+trgt).offset(),
target_top = target_offset.top;
$('html, body').animate({scrollTop:target_top}, 500);
/* Remove active class on any li when an anchor is clicked */
main.children().removeClass();
/* Add active class to clicked anchor's parent li */
$(this).parent().addClass('active');
});
$(window).scroll(function(event) {
if($("#work-section").offset().top < $(window).scrollTop() + $(window).outerHeight()){
$("#work-section1").parent().addClass('active');
$("#about-section1").parent().removeClass('active');
$("#footer-section").parent().removeClass('active');
$("#services-section1").parent().removeClass('active');
$("#process-section1").parent().removeClass('active');
}
if($("#about-section").offset().top < $(window).scrollTop() + $(window).outerHeight()) {
$("#about-section1").parent().addClass('active');
$("#work-section1").parent().removeClass('active');
$("#footer-section1").parent().removeClass('active');
$("#services-section1").parent().removeClass('active');
$("#process-section1").parent().removeClass('active');
}
if($("#services-section").offset().top < $(window).scrollTop() + $(window).outerHeight()){
$("#services-section1").parent().addClass('active');
$("#about-section1").parent().removeClass('active');
$("#work-section1").parent().removeClass('active');
$("#footer-section1").parent().removeClass('active');
$("#process-section1").parent().removeClass('active');
}
if($("#process-section").offset().top < $(window).scrollTop() + $(window).outerHeight()){
$("#process-section1").parent().addClass('active');
$("#about-section1").parent().removeClass('active');
$("#work-section1").parent().removeClass('active');
$("#footer-section1").parent().removeClass('active');
$("#services-section1").parent().removeClass('active');
}
if($("#footer-section").offset().top < $(window).scrollTop() + $(window).outerHeight()){
$("#footer-section1").parent().addClass('active');
$("#about-section1").parent().removeClass('active');
$("#work-section1").parent().removeClass('active');
$("#services-section1").parent().removeClass('active');
$("#process-section1").parent().removeClass('active');
}
});
and the HTML:
<nav id="mainmenu" name="mainmenu">
<ul>
<li><a class="scroll" id="work-section1" href="#work-section">Works</a></li>
<li><a class="scroll" id="about-section1" href="#about-section">About</a></li>
<li><a class="scroll" id="services-section1" href="#services-section">Services</a></li>
<li><a class="scroll" id="process-section1" href="#process-section">Process</a></li>
<li><a class="scroll" id="footer-section1" href="#footer-section">Contact</a></li>
</ul>
</nav>
<section id="about-section" data-anchor-offset="90">
<section id="work-section" data-anchor-offset="90">
...ect...
Could this somehow be adapted to accomplish the effect I am looking for? Or any other/better methods, or plugins I should be looking at?
I should add that the icons use the sprites method which could make the CSS side of things a little trickier, although I would be willing to change them to non-sprite images if necessary...
You could use a small little function for this, that checks if a element is on screen. I set up a little JSFiddle for you: http://jsfiddle.net/LHrkB/1/
Code:
function isElementVisible(elementToBeChecked)
{
var TopView = $(window).scrollTop();
var BotView = TopView + $(window).height();
var TopElement = $(elementToBeChecked).offset().top;
var BotElement = TopElement + $(elementToBeChecked).height();
return ((BotElement <= BotView) && (TopElement >= TopView));
}
$(window).scroll(function () {
isOnView = isElementVisible(".inview");
if(isOnView){
//What to do when element is visible
$(".inview").css({"background":"#ccc"});
}else{ // If not visible
}
});
Ok, so i have changed the JSFiddle a bit, now it uses a fadeIn on a invisible element when it comes into view: http://jsfiddle.net/LHrkB/2/
Ok, i changed the JSFiddle once again. When you scroll in the results pane, and you play around with it a bit you can see the element change class as it comes on screen and also when it goes away again. I commented the JS so you can see what it does and where it does it. http://jsfiddle.net/LHrkB/4/
Thanks to the help of Veritas87 (who is super awesome), managed to get it all working with the following code:
function isElementVisible(elementToBeChecked)
{
var TopView = $(window).scrollTop();
var BotView = TopView + $(window).height();
var TopElement = $(elementToBeChecked).offset().top;
var BotElement = TopElement + $(elementToBeChecked).height();
return ((BotElement <= BotView) && (TopElement >= TopView));
}
$(window).scroll(function () {
isOnView = isElementVisible(".about-icon");
if(isOnView){
//What to do when element is visible
$('.about-icon').addClass('about-icon-active');
}else{ // If not visible
$('.about-icon').removeClass('about-icon-active');
}
isOnView = isElementVisible(".works-icon");
if(isOnView){
//What to do when element is visible
$('.works-icon').addClass('works-icon-active');
}else{ // If not visible
$('.works-icon').removeClass('works-icon-active');
}
isOnView = isElementVisible(".services-icon");
if(isOnView){
//What to do when element is visible
$('.services-icon').addClass('services-icon-active');
}else{ // If not visible
$('.services-icon').removeClass('services-icon-active');
}
isOnView = isElementVisible(".process-icon");
if(isOnView){
//What to do when element is visible
$('.process-icon').addClass('process-icon-active');
}else{ // If not visible
$('.process-icon').removeClass('process-icon-active');
}
});
with the "...icon-active" classes of course containing the style for the icon hover states.
I am building a website which can be viewed here: argit.bounde.co.uk
I have done the majority of the content and I am now trying to work on the navigation. I have three navigation bars (only one is ever visible) and need this method to work no matter which is showing. If you resize your browser to make your window narrower that will show a second, and then when you scroll the navigation that appears is a third.
I have got it working to a fashion but the problem is when I click a link it jumps to where it wants to go momentarily, then returns and then scrolls as it is meant to. This is because of the "href="#target" that i have left in the nav. I have tried including a "return false" but then if the broswer doesnt support JS then the navigation doesnt work at all.
The next problem is I want a way to make the target "over". Currently when you click a link it scrolls to the selected one and the nav updates which link is "over" as it passes them. I want this for when the user is scrolling up and down the page, but if they click a link I want that link to be "over" (and the respective links from other navigations) and not be affected by the scroll checks that would normally override it.
The solution I am using for my onClick navigation is below, I know there are plug ins that will do this kind of thing but I want to write it myself so i can get a better understanding of jQuery. Im not sure if the solution I am using at the moment is a good one, if not please advise me:
function navigation() {
$('html, body').stop().animate({
scrollTop: $($(this).attr('href')).offset().top
}, 1500);
}
The solution I am using for the scrolling checks I found by accident and is below, It works by over riding the equation above it and is actually quite simple. There is also an action to fix the navigation when scrolling.
function navCheck() {
var documentHeight = $(document).height();
var windowHeight = $(window).height();
var windowTop = $(window).scrollTop() + 100;
var navTop = $("#scrollanchor").offset().top;
var mobileTop = $("#mobileanchor").offset().top;
var mobileHeight = $("#mobileanchor").height();
var overviewTop = $("#slider").offset().top;
var bioTop = $("#bio").offset().top;
var solutionsTop = $("#solutions").offset().top;
var experianceTop = $("#experiance").offset().top;
var contactTop = $("#contact").offset().top;
if($(window).scrollTop() > navTop) {
$("#leftfixer").addClass("leftfix");
} else {
$("#leftfixer").removeClass("leftfix");
}
if($(window).width() < 1200){
if(windowTop - 90 > mobileTop + mobileHeight) {
$("#mobilefix").slideDown();
} else {
if(windowTop - 96 <= mobileTop) {
$("#mobilefix").hide();
}
}
} else {
$("#mobilefix").slideUp();
}
$("li").removeClass("over");
$("li.navoverview").addClass("over");
if(windowTop > bioTop) {
$("li").removeClass("over");
$("li.navbio").addClass("over");
}
if(windowTop > solutionsTop) {
$("li").removeClass("over");
$("li.navsolutions").addClass("over");
}
if(windowTop > experianceTop) {
$("li").removeClass("over");
$("li.navexperiance").addClass("over");
}
if(windowTop > contactTop || windowTop > documentHeight - windowHeight) {
$("li").removeClass("over");
$("li.navcontact").addClass("over");
}
}
This is my first post here so if I have missed out any information Im sorry! I have also looked for similar posts but it seems most people go for a plugin when doing this kind of thing. Thank you
UPDATE: The page jumping to the anchor has been fixed
Normally, if you use JavaScript to scroll the page, you would put return false; after calling the function. This prevents the page from scrolling momentarily to the anchor.
Something like
function navigation() {
$('html, body').stop().animate({
scrollTop: $($(this).attr('href')).offset().top
}, 1500);
return false;
}
I have a strange issue that might have to do with jQuery document ready. Below is an html and script block that contains the usual social networking scripts. The Javascript block below displays the dd_outer div on the left edge of the body div, and when the browser window is shrunk, the div is faded out and the dd_footer div is faded in. The fadein and fadeout between the two divs works OK.
The problem is two fold: one issue is when the browser window is full width (1200px+), the Facebook script will not load and display consistently; it sometimes appears and sometimes doesn't, sometimes after a page reload and sometimes doesn't. (No browser or .htaccess caching is involved). Only the Facebook share fails to show consistently; all other services show OK.
The second problem that when the browser window is narrow - 650 px or so, when the dd_outer div is not displayed and the dd_footer div is - the footer div will not show on a page reload until the browser window is moved the smallest amount. Then the the div will display, Facebook share and all. For a mobile device, this is a problem because the browser window will be narrow to begin with and shouldn't need to be "nudged" to make the dd_footer div display.
This problem may have come into play because I have adapted this code from a WordPress plugin that used options to set the position of the dd_outer div and scroll height. That's the reason for the variables above the document ready call.
Is this the issue with what seems to be a document ready issue?
How can the variables be integrated into the script itself? It doesn't matter if they are hardcoded; I can change them when needed.
I'd throw this in a jsfiddle to demo but the divs won't realistically float with the window resizing.
I haven't included the CSS for clarity.
This is the html and social script block:
<div class='dd_outer'><div class='dd_inner'><div id='dd_ajax_float'>
<div class="sbutton"><script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script><fb:like layout="box_count" show_faces="false" font=""></fb:like></div>
<div class="sbutton">
Tweet<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div>
<div class="sbutton"><script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script><g:plusone size="tall"></g:plusone></div>
<div class="sbutton"><script src="http://platform.linkedin.com/in.js" type="text/javascript"></script>
<script type="IN/Share" data-counter="top"></script></div>
</div></div></div>
In the footer is <div id="dd_footer">that contains the same social scripts as above</div> and are faded in and out by the script below:
This is the jQuery that positions the dd_outer social services to the left and fades it out and fades in the dd_footer div.
<script type="text/javascript">
var dd_top = 0;
var dd_left = 0;
var dd_offset_from_content = 70; var dd_top_offset_from_content = 10;
jQuery(document).ready(function(){
var $floating_bar = jQuery('#dd_ajax_float');
var $dd_start = jQuery('#dd_start');
var $dd_end = jQuery('#dd_end');
var $dd_outer = jQuery('.dd_outer');
// first, move the floating bar out of the content to avoid position: relative issues
$dd_outer.appendTo('body');
dd_top = parseInt($dd_start.offset().top) + dd_top_offset_from_content;
if($dd_end.length){
dd_end = parseInt($dd_end.offset().top);
}
dd_left = -(dd_offset_from_content + 55);
dd_adjust_inner_width();
dd_position_floating_bar(dd_top, dd_left);
$floating_bar.fadeIn('slow');
if($floating_bar.length > 0){
var pullX = $floating_bar.css('margin-left');
jQuery(window).scroll(function () {
var scroll_from_top = jQuery(window).scrollTop() + 30;
var is_fixed = $dd_outer.css('position') == 'fixed';
if($dd_end.length){
var dd_ajax_float_bottom = dd_end - ($floating_bar.height() + 30);
}
if($floating_bar.length > 0)
{
if(scroll_from_top > dd_ajax_float_bottom && $dd_end.length){
dd_position_floating_bar(dd_ajax_float_bottom, dd_left);
$dd_outer.css('position', 'absolute');
}
else if ( scroll_from_top > dd_top && !is_fixed )
{
dd_position_floating_bar(30, dd_left);
$dd_outer.css('position', 'fixed');
}
else if ( scroll_from_top < dd_top && is_fixed )
{
dd_position_floating_bar(dd_top, dd_left);
$dd_outer.css('position', 'absolute');
}
}
});
}
});
jQuery(window).resize(function() {
dd_adjust_inner_width();
});
var dd_is_hidden = false;
var dd_resize_timer;
function dd_adjust_inner_width() {
var $dd_inner = jQuery('.dd_inner');
var $dd_floating_bar = jQuery('#dd_ajax_float')
var width = parseInt(jQuery(window).width() - (jQuery('#dd_start').offset().left * 2));
$dd_inner.width(width);
var dd_should_be_hidden = (((jQuery(window).width() - width)/2) < -dd_left);
var dd_is_hidden = $dd_floating_bar.is(':hidden');
if(dd_should_be_hidden && !dd_is_hidden)
{
clearTimeout(dd_resize_timer);
dd_resize_timer = setTimeout(function(){ jQuery('#dd_ajax_float').fadeOut(); }, -dd_left);
jQuery('#dd_footer').fadeIn();
}
else if(!dd_should_be_hidden && dd_is_hidden)
{
clearTimeout(dd_resize_timer);
dd_resize_timer = setTimeout(function(){ jQuery('#dd_ajax_float').fadeIn(); }, -dd_left);
jQuery('#dd_footer').fadeOut();
}
}
function dd_position_floating_bar(top, left, position) {
var $floating_bar = jQuery('#dd_ajax_float');
if(top == undefined) top = 0 + dd_top_offset_from_content;;
if(left == undefined) left = 0;
if(position == undefined) position = 'absolute';
$floating_bar.css({
position: position,
top: top + 'px',
left: left + 'px'
});
}
</script>
jQuery .ready() does not wait for iframes and other external media to load. These social buttons tend to work by inserting an iframe. The load event does wait for iframes etc, so you could try using that event instead, i.e.
jQuery(window).load(function () {
/* put the code you had inside .ready() here */
});
The problem comes with your idea: $(document).ready() fires when the DOM is ready, not when all scripts are ready!
an idea would be to search for trigger of that social-APIs you are using or just delay your calculations (e.g. via setTimeout).
Keep in mind that they are asyncron, even if you specify "async" on the script-tag to be false, you still dont know when they will activate or are finished.
I suggest to use the standard DOM event window.onload if you want to make sure that all the external assets, scripts, images, etc. are loaded first before you do something:
window.onload = function () {
// your script that needs to run after all the external assets are loaded
}
Reference: https://developer.mozilla.org/en-US/docs/DOM/window.onload
I just ran into similar problems with the facebook script... I just used the integration in the HEAD-section with javascript and added an "asynchronous"-attribute to the javascript-embedding script which then fires an asynchronous "heeey, facebook is ready now, too"-event to my jQuery-eventqueue...
I can't help you in detail, because I don't totally understand what you WANT to do and would reorganize the whole code A LOT... so - contact me private (email/skype) or try figuring out... I used that lines of code: pastie.org/private/9m4b9eet1dzzkl6duqpkrg
I have a vertically-scrolling div within a page that also scrolls vertically.
When the child div is scrolled with the mouse wheel and reaches the top or bottom of the scroll bar, the page (body) begins to scroll. While the mouse is over the child div, I'd like the page (body) scroll to be locked.
This SO post (scroll down to the selected answer) demonstrates the problem well.
This SO question is essentially the same as mine, but the selected answer causes my page contents to noticeably shift horizontally as the scrollbar disappears and reappears.
I thought there might be a solution that leverages event.stopPropagation(), but couldn't get anything to work. In ActionScript, this kind of thing would be solved by placing a mousewheel handler on the child div that calls stopPropagation() on the event before it reaches the body element. Since JS and AS are both ECMAScript languages, I thought the concept might translate, but it didn't seem to work.
Is there a solution that keeps my page contents from shifting around? Most likely using stopPropagation rather than a CSS fix? JQuery answers are welcome as is pure JS.
here's what i ended up with. very similar to #mrtsherman's answer here, only pure JS events instead of jQuery. i still used jQuery for selecting and moving the child div around, though.
// earlier, i have code that references my child div, as childDiv
function disableWindowScroll () {
if (window.addEventListener) {
window.addEventListener("DOMMouseScroll", onChildMouseWheel, false);
}
window.onmousewheel = document.onmousewheel = onChildMouseWheel;
}
function enableWindowScroll () {
if (window.removeEventListener) {
window.removeEventListener("DOMMouseScroll", onArticleMouseWheel, false);
}
window.onmousewheel = document.onmousewheel = null;
}
function onChildMouseWheel (event) {
var scrollTgt = 0;
event = window.event || event;
if (event.detail) {
scrollTgt = -40 * event.detail;
} else {
scrollTgt = event.wheelDeltaY;
}
if (scrollTgt) {
preventDefault(event);
$(childDiv).scrollTop($(childDiv).scrollTop() - scrollTgt);
}
}
function preventDefault (event) {
event = event || window.event;
if (event.preventDefault) {
event.preventDefault();
}
event.returnValue = false;
}
i've noticed the scrolling doesn't match normal scrolling exactly; it seems to scroll a bit faster than without this code. i assume i can fix by knocking down wheelDeltaY a bit, but it's odd that it would be reported differently by javascript than it's actually implemented by the browser...
I usually do it with a small hack listening to the scroll event on the document: it resets the scroll height back to the original one - effectively freezing the document from scrolling but any inner element with overflow: auto will still scroll nicely:
var scrollTop = $(document).scrollTop();
$(document).on('scroll.scrollLock', function() {
$(document).scrollTop(scrollTop);
});
and then when I'm done with the inner scroll lock:
$(document).off('scroll.scrollLock');
the .scrollLock event namespace makes sure I'm not messing with any other event listeners on scroll.
Although this is an old question, here is how I do it with jQuery. This allows you to scroll a list within an outer list, or you can change the outer list to the document to do what the OP asked.
window.scrollLockHolder = null;
function lockScroll(id){
if (window.scrollLockHolder == null){
window.scrollLockHolder = $('#' + id).scrollTop();
}
$('#' + id).on('scroll', function(){
$('#' + id).scrollTop(window.scrollLockHolder);
});
}
function unlockScroll(id){
$('#' + id).off('scroll');
window.scrollLockHolder = null;
}
And you can use it like this:
<ul onmousemove="lockScroll('outer-scroller-id')" onmouseout="unlockScroll('outer-scroller-id')">
<li>...</li>
<li>...</li>
</ul>
what about this:
div.onmousemove = function() { // may be onmouseover also works fine
document.body.style.overflow = "hidden";
document.documentElement.style.overflow = "hidden";
};
div.onmouseout = function() {
document.body.style.overflow = "auto";
document.documentElement.style.overflow = "auto";
};
i wonder if it is possible to create a bookmarklet to click on and the current webpage scrolls to the bottom!
javascript:function%20scrollme(){dh=document.body.scrollHeight;ch=document.body.clientHeight;if(dh>ch){moveme=dh-ch;window.scrollTo(0,moveme);}}
if i create a new bookmark and paste this as address nothing happens. I actually have no idea how to run javascript within a bookmarklet, however i just bookmarked the css-tricks Printliminator
maybe you could help, i would love to have a bookmarklet like this!
First, your JavaScript only defines a function and does nothing else.
Second, you need to use document.documentElement (which represents the <html> element) instead of document.body:
javascript:dh=document.documentElement.scrollHeight;ch=document.documentElement.clientHeight;if(dh>ch){moveme=dh-ch;window.scrollTo(0,moveme);}
or, simply
javascript:window.scrollTo(0,document.documentElement.scrollHeight)
(apparently it doesn't matter if y-coord of window.scrollTo is greater than the maximum position).
Update: In case you have to deal with IE in quirks mode, the root element is indeed document.body. Other browsers let document.documentElement.clientHeight represent the document's height (see Finding the size of the browser window, which deals with the window's height, but contains a nice table). Anyway, you want to set the position of the scroller to whatever is the greatest of the three:
javascript:window.scrollTo(0,Math.max(document.documentElement.scrollHeight,document.body.scrollHeight,document.documentElement.clientHeight))
Here is a function that smoothly scrolls down to the bottom of a page:
function scroll(scroll_to) {
if (scroll.timer) clearTimeout(scroll.timer);
var scroll_current = document.body.scrollTop,
distance = Math.abs(scroll_current - scroll_to);
if (scroll_current == scroll_to) return;
if (scroll_current > scroll_to) {
if (distance < 5) {
scroll_current -= distance;
} else {
scroll_current -= Math.ceil(distance / 10);
}
}
if (scroll_current < scroll_to) {
if (distance < 5) {
scroll_current += distance;
} else {
scroll_current += Math.ceil(distance / 10);
}
}
document.body.scrollTop = scroll_current;
scroll.timer = setTimeout(function() {
scroll(scroll_to)
}, 10);
}
If you call it:
scroll(document.body.scrollHeight - innerHeight);
it will scroll to the bottom of the page.
You can also use it to scroll to the top of the page like this:
scroll(0);
Just attach it to a button or link's onclick event.
you can simply use an anchor with this syntax
<a name="label">Any content</a>
and
Any content