I want to update the class of a link when the window scrolls into the linked container. It works fine on some browsers, but the browser crashes on iOS.
Any idea why or how I can improve on this?
HTML:
<ul id="section-follow" data-default="background-none" data-active="background-red">
<li class="">First</li>
<li class="">Second</li>
<li class="">Third</li>
<li class="">Fourth</li>
</ul>
Javascript:
function SectionFollow() {
if ($('#section-follow').length) {
var sections = {},
_height = $(window).height(),
i = 0;
var classDefault = $('#section-follow').attr('data-default');
var classActive = $('#section-follow').attr('data-active');
$('.follow').each(function() {
sections[this.id] = $(this).offset().top;
});
var pos = $(this).scrollTop();
for (i in sections) { // Each section
if (sections[i] < pos + (_height * .5)) { // 50% from top
$('#section-follow li a').removeClass(classActive).addClass(classDefault);
$('a[href="#'+i+'"]').removeClass(classDefault).addClass(classActive); //
if (history.pushState) { // Update the URL hash
history.pushState(null, null, window.location.origin + window.location.pathname + window.location.search + $('a[href="#'+i+'"]').attr('href')); // Set the hash using the history api if available to tackle Chromes repaint bug on hash change
} else {
window.location.hash = $('a[href="#'+i+'"]').attr('href');
// Otherwise fallback to the hash update for sites that don't support the history api
}
} // End if 50% from the top
} // End each section
} // End if section follow exists
} // End SectionFollow
$(window).scroll(function() {
SectionFollow();
});
Related
I have multiple divs on my page with different classes
how can I check which div is scrolled to the top and therefore do something.
<div class="menu-session-0">
content
</div>
<div class="menu-session-1">
content
</div>
<div class="menu-session-2">
content
</div>
I already tried this :
$(window).scroll(function() {
setTimeout(function(){
var hT = $('.session-index_1').offset().top,
hH = $('.session-index_1').outerHeight(),
wH = $(window).height(),
wS = $(this).scrollTop() + 175;
if(hT <= (wS + 250)){
$('.menu-item').removeClass('menu-item_active');
$('.item-index-1').addClass('menu-item_active');
mySwiper.slideTo(1);
mySwiper.update();
}
},1050);
});
But it did not work as I expected...
Ok.. I get it now, I think.. :P
This should (in theory) work just fine if you copy and paste instead of your code.
If you really want to use JQuery just replace the element reference getters and class writers
const menuItems = document.getElementsByClassName('menu-item');
const menuSessions = document.getElementsByClassName('menu-session');
var nextSession, activeSession;
// do this to get menu-session offsets.. rerun function on viewport resize
function getSessionOffset() {
for( let session of menuSessions ) {
// store position and dataset.index directy on the element reference
session.y = session.offsetTop;
session.indx = session.dataset.index;
}
// define active elements which position we listen for
// these are correct if the window scroll offset is 0 (or at the top)
activeSession = menuSessions[0];
nextSession = menuSessions[1];
onScroll(window.pageYOffset); // so we check and set the correct active elements
}
getSessionOffset();
// page scroll listener
window.addEventListener( 'scroll' , ScrollHandler );
var lastScrollPos = 0; // last recorded window scroll offset
var scrollTick = false; // the tick is used to throttle code execution
function ScrollHandler (ev) {
lastScrollPos = ev.target.scrollTop;
if (!scrollTick) {
window.requestAnimationFrame(() => { // <- read up on this if you are not familiar. Very usefull
onScroll(lastScrollPos);
scrollTick = false;
});
scrollTick = true;
}
}
function onScroll(scrollY) {
if (scrollY > nextSession.y) { // if lower offset is less than scrollPos
removeActiveClass(activeSession.indx); // we remove the active class from menu item
activeSession = menuSessions[nextSession.indx]; // define new elements to listen for
nextSession = menuSessions[nextSession.indx + 1];
addActiveClass(activeSession.indx); // and add an active class to the new menu item
} else if (scrollY < activeSession.y) { // do the same here only in reverse
removeActiveClass(activeSession.indx);
nextSession = menuSessions[activeSession.indx];
activeSession = menuSessions[nextSession.indx - 1];
addActiveClass(activeSession.indx);
}
}
function removeActiveClass(indx) {
menuItems[indx].classList.remove('menu-item_active');
}
function addActiveClass(indx) {
menuItems[indx].classList.add('menu-item_active');
}
We listen only for the current and next values.
This may look like a lot but can be shortened to a third of the size.
Hope this helps :)
When I click I on load more link it load more results I need to change it from when I click to link when I Scroll To AJAXloaded Div
My Html Code
<div id="AJAXloaded">
<div class="loadmore">
<ul class="pagination">
<li class="active">
<a id="load-more">مشاهدة المزيد ...
<img id="lodingGif" src="{{Request::root()}}/public/upload/logo/loading.gif" height="25" width="25" style="display: none;">
</a>
</li>
</ul>
</div>
</div>
My JS Code
$(document).on("click","#load-more",function() {
page=page+1;
loadMoreData(page);
});
This is a perfect use case for the IntersectionObserver API
From the docs:
The Intersection Observer API provides a way to asynchronously observe
changes in the intersection of a target element with an ancestor
element or with a top-level document's viewport. [...] Intersection
information is needed for many reasons, such as:
Implementing "infinite scrolling" web sites, where more and more content is loaded and rendered as you scroll, so that the user doesn't
have to flip through pages.
var options = {
root: document.querySelector('#AJAXloaded'),
rootMargin: '0px',
threshold: 0,
};
var observer = new IntersectionObserver(callback, options);
function callback() {
// element scrolled into viewport
}
Browser support is quite good (except for IE). If you need to support older browsers there are a couple of polyfills available. But since they use polling and don't work in every situation I would personally just leave the button for those browsers and remove it for browsers supporting IntersectionObserver.
var supportsIntersectionObserver = 'IntersectionObserver' in window;
if(supportsIntersectionObserver) { button.remove(); }
How to add infinite scrolling to Blogger blogs?
If you don’t care about the details and you only want to enable the feature on your blog, click on the button below and add the code to your blog. Infinite scrolling should just work on your blog, in most cases. If you have a custom template, though, you may need to tweak the code a little. (Scroll down to “Frequently asked question” section for details.
If clicking on this button does nothing, or it doesn’t work for some reason, you can add this code manually:
Add a HTML/JavaScript gadget to your blog.
Leave the gadget’s title empty.
Paste the following code as the gadget’s content:
(function($) {
var loadingGif = 'https://lh3.googleusercontent.com/-FiCzyOK4Mew/T4aAj2uVJKI/AAAAAAAAPaY/x23tjGIH7ls/s32/ajax-loader.gif';
var olderPostsLink = '';
var loadMoreDiv = null;
var postContainerSelector = 'div.blog-posts';
var loading = false;
var win = $(window);
var doc = $(document);
// Took from jQuery to avoid permission denied error in IE.
var rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;
function loadDisqusScript(domain) {
$.getScript('http://' + domain + '.disqus.com/blogger_index.js');
}
function loadMore() {
if (loading) {
return;
}
loading = true;
if (!olderPostsLink) {
loadMoreDiv.hide();
return;
}
loadMoreDiv.find('a').hide();
loadMoreDiv.find('img').show();
$.ajax(olderPostsLink, {
'dataType': 'html'
}).done(function(html) {
var newDom = $('<div></div>').append(html.replace(rscript, ''));
var newLink = newDom.find('a.blog-pager-older-link');
var newPosts = newDom.find(postContainerSelector).children();
$(postContainerSelector).append(newPosts);
// Loaded more posts successfully. Register this pageview with
// Google Analytics.
if (window._gaq) {
window._gaq.push(['_trackPageview', olderPostsLink]);
}
// Render +1 buttons.
if (window.gapi && window.gapi.plusone && window.gapi.plusone.go) {
window.gapi.plusone.go();
}
// Render Disqus comments.
if (window.disqus_shortname) {
loadDisqusScript(window.disqus_shortname);
}
// Render Facebook buttons.
if (window.FB && window.FB.XFBML && window.FB.XFBML.parse) {
window.FB.XFBML.parse();
}
// Render Twitter widgets.
if (window.twttr && window.twttr.widgets && window.twttr.widgets.load) {
window.twttr.widgets.load();
}
if (newLink) {
olderPostsLink = newLink.attr('href');
} else {
olderPostsLink = '';
loadMoreDiv.hide();
}
loadMoreDiv.find('img').hide();
loadMoreDiv.find('a').show();
loading = false;
});
}
function getDocumentHeight() {
return Math.max(
win.height(),
doc.height(),
document.documentElement.clientHeight);
}
function handleScroll() {
var height = getDocumentHeight();
var pos = win.scrollTop() + win.height();
if (height - pos < 150) {
loadMore();
}
}
function init() {
if (_WidgetManager._GetAllData().blog.pageType == 'item') {
return;
}
olderPostsLink = $('a.blog-pager-older-link').attr('href');
if (!olderPostsLink) {
return;
}
var link = $('Load more posts');
link.click(loadMore);
var img = $('<img src="' + loadingGif + '" style="display: none;">');
win.scroll(handleScroll);
loadMoreDiv = $('<div style="text-align: center; font-size: 150%;"></div>');
loadMoreDiv.append(link);
loadMoreDiv.append(img);
loadMoreDiv.insertBefore($('#blog-pager'));
$('#blog-pager').hide();
}
$(document).ready(init);
})(jQuery);
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;
}
I got a one-page website, before that it had 3 different pages, and the navigation bar's link to the current page turned to ´id="selected´
#selected {
background-color:white;
color: #645406;
cursor: default;
}
when you are on that page.
Now it's a bit harder, as the links work just as anchor links.
I'd need a script that would detect where the user is scrolling, and automatically turn the anchor's link to ´id="selected"´ when the user scrolls over the anchor.
Example:
http://jsfiddle.net/mbSXB/
Try this http://jsfiddle.net/8NKqf/1/
$(function() {
var anchors = $('.anchor');
var navLinks = $('.navigointi a');
$(window).scroll(function() {
var scrollTop = $(window).scrollTop();
var clientHeight = document.documentElement.clientHeight;
var activeSectionAnchor, hash;
anchors.each(function() {
if ($(this).offset().top < scrollTop + clientHeight) {
activeSectionAnchor = $(this);
}
});
hash = "#" + activeSectionAnchor.attr('name');
activeLink = navLinks.removeClass('selected').filter('[href="' + hash + '"]');
activeLink.addClass('selected');
});
});
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;
}
});
});