I need to make sure that a specific line is visible when the user loads the page, even if that line appears many lines later in the page. In other words, when the page loads, it instantly jumps to that marked line. The line simply needs to be somewhere within the visible screen of the browser window. It is marked like this:
<div class="scrollhere">This line should always be visible when the page loads.</div>
Only one of these appears on the whole page.
I cannot edit the HTML marking the text, but can edit the CSS and JavaScript at the beginning of the document.
I do not mean for just that specific line to be moved, i.e. with position:absolute, but that the page loads to that line.
How can I make the page load mid-way down the page, wherever a specific <div class="scrollhere"> is found?
So if you can't edit the HTML at all, then you'll need to use JavaScript or jQuery. This is a function that I've used to scroll to an element.
Javascript
This will query the DOM for all elements that have your class scrollhere. And we'll select just the first item in the array (to go to the first element). Then we'll use the scrollIntoView().
document.getElementsByClassName('scrollhere')[0].scrollIntoView()
jQuery
/*
* A scrollTo function that gets passed a selector (ID, class, element) & an optional offset
*
* #param {string} location ('#resultsTop')
* #param {integer} offset (-100)
*/
function scrollTo(location, offset) {
$("html, body").animate({scrollTop: ($(location).offset().top + (offset || 0) )}, "slow");
return false;
};
In your case, you'd call this function as follows:
scrollTo('.scrollhere');
You could also optionally pass in an offset. So maybe you don't want to scroll to the top of that element. But would rather scroll to 100 pixels above it.
scrollTo('.scrollhere',-100);
And being that you can't edit the HTML, then you'll definitely want to use a listener.
$(document).ready(function(){
scrollTo('.scrollhere',-100);
});
This is a pure JavaScript example. No jQuery required.
Here is a simple example of this answer in JSFiddle.
window.onload = function() {
var divs = document.getElementsByTagName("div");
for (var i in divs) {
if (divs[i].className.indexOf("class2") > -1) {
//Finds top of of the element
var top = 0;
var obj = divs[i];
if (obj.offsetParent) {
do {
top += obj.offsetTop;
} while (obj = obj.offsetParent);
}
//Scroll to location
window.scroll(0, top);
break;
}
}
};
This snippet may help you
$('html, body').animate({
scrollTop: ($('.class').first().offset().top)
},500);
Related
I have an empty HTML page with just a div and a button to load data.
<body>
<div id="main_div"></div>
<button class="load_more" onclick="myFunction()">Load More..</button>
</body>
myFunction is a function which creates and populate the div main_div.
I want to create an infinite scroll view out of it.
I have added scroll listener which call myFunction and loads the data once only 200px is left. (I got this code from a blog online)
$(window).on('scroll', function() {
console.log('Scroll detected')
var scrollHeight = $(document).height();
var scrollPos = Math.floor($(window).height() + $(window).scrollTop());
var isBottom = scrollHeight - 200 < scrollPos;
if (isBottom && currentscrollHeight < scrollHeight) {
$('.load_more').click();
currentscrollHeight = scrollHeight;
}
});
The infinite scroll works fine when I manually tap the load button a few times and the page is loaded with elements. After that when I scroll, it loads new data.
But what I want is to fill the initial space from the same API.
I thought of calling the same function multiple time, but I am not sure how many times should I call, as the page can be opened from a mobile or any browser and I would not know the height of that.
Also, I want to avoid JQuery or other frameworks to keep it minimal. I know the code is already using JQuery, but I plan to remove it.
you can call a function which will check your div height and compare it with windowHeight. Till div height less than windowHeight you should call that function
Like
document.addEventListener("DOMContentLoaded", function(e) {
function loadData(callBackFunction) {
// your function body
// in your ajax success request add following line
if (callBackFunction) {
callBackFunction();
}
}
function loadInitialData() {
if (document.getElementById('main_div').getBoundingClientRect().height < window.innerHeight) {
loadData(loadInitialData);
}
}
(function() {
loadInitialData();
})();
// Also Your click event won't work here(inside the listener function).
// So it isn't visible outside of this function's scope.
// If you want to call the method directly 'from the button', then you have to attach it to `window`
window.loadData = loadData;
})
I'm already busy with a one page navigation. Below you will find more information about the problem and my wish.
How should it work?
Once a navigation item is clicked, scroll to its particular section and update the active class of the menu. And if the page is scrolled to its particular section, it should update the active state too - so change the class to its particular anchor.
Fixed header
For the website I also used a fixed header, so this should NOT be overlay the particular section. So the menu should stop scrolling when the bottom of the header is reaching the top of the section.
Variable sections
All sections on the one page design has a different height.
Problem
I have already tried a lot of code, to get it work. Most of the code is working, but this isn’t in all browsers the same. Also I have some trouble with updating the active state of the particular section - that match to the active anchor.
Code
I used jQuery to create a smooth scroll to anchor. You can see my working code at JSfiddle.
Here are all resources:
JS
Here I controle the click function of the navigation.
So when the user click a list item of #primary-navwrapper, then change the active state class and scroll to the particular section, that match with the clicked anchor.
$('#primary-navwrapper li').find('a[href^="#"]').click(function(event) {
// Prevent from default action to intitiate
event.preventDefault();
$('#primary-navwrapper li a').removeClass("current");
$(this).addClass("current");
// The id of the section we want to go to.
var anchorId = $(this).attr("href");
// Our scroll target : the top position of the
// section that has the id referenced by our href.
var target = $(anchorId).offset().top - offset;
//console.log(target);
$('html, body').animate({ scrollTop: target }, 500, function () {
//window.location.hash = '!' + id;
window.location.hash = anchorId;
});
});
Beside the click function, I also want that when the user scrolls around the one page, it will automatically update the active statement.
function setActiveListElements(event){
// Get the offset of the window from the top of page
var windowPos = $(window).scrollTop();
$('#primary-navwrapper li a[href^="#"]').each(function() {
var anchorId = $(this);
var target = $(anchorId.attr("href"));
if (target.length > 0) {
if (target.position().top <= windowPos && target.position().top + target.height() > windowPos) {
$('#primary-navwrapper li a').removeClass("current");
anchorId.addClass("current");
}
}
});
}
$(window).scroll(function() {
setActiveListElements();
});
In above code, I think that the line of if (target.position().top <= windowPos && target.position().top + target.height() > windowPos) isn’t correct and maybe to long..
If there are any questions or something, I like to hear from you.
Casper
Looking at your code, I've updated the line you said for the below one:
if (target.position().top - $('#header').outerHeight() <= windowPos) {
$('#primary-navwrapper li a').removeClass("current");
anchorId.addClass("current");
}
In this way, it'll get the target's difference to the top minus the header's height (as it will be always visible) and then check with the window's position. If it's inferior, the user has passed this anchor, so the correspondent link in the menu gets highlighted.
And your first link doesn't have its anchor in the fiddle, so:
<li>Home</li>
After these changes, everything seems to work fine.
JSFiddle: http://jsfiddle.net/8n06pvy9/17/
EDIT:
To update the hash, I've tried to use a simple window.location.hash = anchorId;, but it results in some weird scroll issues in FF and IE. I've spent some time on it, but I wasn't able to figure out what happens.
So, I suggest a trick that I've already used, using #! in the hash. In this way, your code would be like that:
window.location.hash = '#!' + anchorId.replace('#', '');
And in the scroll function, like that:
window.location.hash = '#!' + anchorId.attr('href').replace('#', '');
JSFiddle: http://jsfiddle.net/8n06pvy9/18/
And, if you want, you can check for the hash in pageload, remove the exclamation point and scroll the page to the desired anchor. Or, if you want to avoid all of that, you can always use some history plugins, like this one. In your case, I personally wouldn't use a plugin for that, but it's your call if it worth it or not.
Hope it helps!
I have this code and it works exactly as I want. The menu bar sits on top and recognizes the section it is on or in. You can click the links in the yellow menu to move between the sections.
Demos: http://jsfiddle.net/spadez/2atkZ/9/ and http://jsfiddle.net/spadez/2atkZ/9/embedded/result/
$(function () {
var $select = $('#select');
var $window = $(window);
var isFixed = false;
var init = $select.length ? $select.offset().top : 0;
$window.scroll(function () {
var currentScrollTop = $window.scrollTop();
if (currentScrollTop > init && isFixed === false) {
isFixed = true;
$select.css({
top: 0,
position: 'fixed'
});
$('body').css('padding-top', $select.height());
} else if (currentScrollTop <= init) {
isFixed = false;
$select.css('position', 'relative');
$('#select span').removeClass('active');
$('body').css('padding-top', 0);
}
//active state in menu
$('.section').each(function(){
var eleDistance = $(this).offset().top;
if (currentScrollTop >= eleDistance-$select.outerHeight()) {
var makeActive = $(this).attr('id');
$('#select span').removeClass('active');
$('#select span.' + makeActive).addClass('active');
}
});
});
$(".nav").click(function (e) {
var divId = $(this).data('sec');
$('body').animate({
scrollTop: $(divId).offset().top - $select.height()
}, 500);
});
});
However, the code itself gets quite laggy as soon as you start putting any content in the boxes. According to help I've received, it's that I am repeatedly changing page layout properties via the animation and querying page layout properties in the scroll handler, thus triggering a large number of forced layouts.
User Tibos said that:
You could get a big improvement by disabling the scroll handler during
the click animation and instead triggering the effects with no checks
made (set the active class on the clicked element).
Could anyone show me how I can achieve this optimization?
Demo page and another concept demo: http://codepen.io/vsync/pen/Kgcoa
The KEY here is to position your select inside another element, so when it get's FIXED to the screen, it won't affect the other elements by the sudden lack of height it once physically occupied. I've added a little CSS also. It's important to use the jQuery version 1.11 or above, because they fixed a but that caused the same class to be added again and again, regardless if an element already has it. bad for performance. Also, I've used a for loop and not jquery each loop on the sections elements, because a for loop is much faster due to the lack of function callback an each function has. Also, a very important thing is to make sure that every element that can be cached is actually cached, so we don't look for it endlessly in the DOM...
For showing which section we're on, I'm looping on all the sections, starting for the last one, which is important to, and checking if each top has passed the window's top using getBoundingClientRect method for knowing such thing. This helps knowing where we are.
var pos,
$el = $('#select'),
navItems = $el.find('.nav'),
menuHeight = $el[0].clientHeight,
scrollY,
sections = $('.section').toArray(), // cache sections elements
pointOfAttachment = $('.jumbo')[0].clientHeight - menuHeight;
// Bind events
$(window).on('scroll.nav', updateNav)
.on('resize.nav', updateNav);
function updateNav(){
scrollY = window.pageYOffset || document.documentElement.scrollTop;
for( var i = sections.length; i--; ){
if( sections[i].getBoundingClientRect().top < 0 ){
navItems.filter('.' + sections[i].id).addClass('active').siblings().removeClass('active');
break;
}
navItems.removeClass('active');
}
if( scrollY > pointOfAttachment )
$el.addClass('fixed');
else
$el.removeClass('fixed');
}
Why is this optimal?
The key for good performance is to do as minimal as possible to achieve your goal, and minimal here means accessing the DOM as little as possible and changing it even less times than accessing it. This is HIGHLY optimal code which ONLY changes the DOM when a change is required and never in any other situation.
You shouldn't need to do this on each scroll event. You could throttle the event and only run the callback something like every 20ms. The user shouldn't notice. You can do this with underscore, or write your own solution.
Another thing that would ease the lagg is to move as much out of the scroll event callback as possible. You don't need to query $('body') all the time for example, save that to a variable.
I have some jquery code that is picking up some issues in firebug chrome.
any help would be great, please update fiddle.
please see the link with fiddle.
http://jsfiddle.net/jwhTd/
image
/* SHOW CATEGORIES
===================================================================*/
$('.normal-btn\.interest').click(function(e){
// Prevent the event from bubbling up the DOM tree
e.stopPropagation();
$('.categories-wrap').fadeIn(); // must be hidden, to fade in
});
$(document, '.normal-btn\.interest').click(function(){
$('.categories-wrap').fadeOut(); // must be visible, to fade out
});
var offset = $(".sticky-header").offset();
var sticky = document.getElementById("sticky-header")
var additionalPixels = 50;
$(window).scroll(function () {
if ($(window).scrollTop() > offset.top - additionalPixels) {
$('#profile-container').addClass('fixed');
} else {
$('#profile-container').removeClass('fixed');
}
});
It's telling you exactly what is wrong. offset is undefined. You probably expect that it has a value, check why it doesn't have one.
You get more errors though. Something about slider and another about an invalid .top access somewhere.
it looks like most of that code is not in the document.ready. the var offset = $(".sticky-header").offset(); needs to be executed once the dom is ready.
Your code:
var offset = $(".sticky-header").offset();
var sticky = document.getElementById("sticky-header")
var additionalPixels = 50;
The first line selects all elements with the class of sticky-header, then gets the offset of the first one. The .offset() function returns undefined in the event that the selector matches zero elements, which appears to be the case here due to the error you're getting later on.
On the next line you're selecting an element with an id of sticky-header, which makes me think that perhaps your first line should be
var offset = $('#sticky-header').offset();
instead, which uses an ID selector rather than a class one.
I have an HTML page with multiple divs. Each div having a section of its own. My requirement is to load the page initially with all the sections. Then to autoscroll the page so that the first div section has its header fixed and contents scrolling. At the end of its contents the second sections header takes up first section headers place and now contents for section 2 scrolls. Then the third sections header takes place of second header and contents for section 3 auto scrolls and so on. At the end of all section it again starts with section 1. Can anybody help me as to how to accomplish this?
Below is my code link,
http://pastebin.com/EAYtsWAT
I am using jsscroller for automatic content scrolling. I am able to scroll the contents but I dont know how to Keep header Activity1 fixed than scroll its contents, then remove that header and replace it with Activity2 header, scroll its contents and so on.
After doing some housekeeping on the code that you provided (and moving it to jsfiddle) here's something that (I think) does what you want.
The jscroller functionality is relatively limited, so I had to apply some tweaks to get it together:
function SectionManager(){
this.currentSection = null;
this.sections = $("#content .section");
this.numSections = this.sections.length;
this.transition = function (current){
//SCROLLER CODE STARTS HERE....
$jScroller.config.refresh = 100;
// Add Scroller Object
$jScroller.config.obj = [];
$jScroller.add(
"#content .section.active .activityTbl",
"#content .section.active .activityTbl > table",
"up",
3
);
// Start Autoscroller
$jScroller.start();
$jScroller.cache.init = true;
//SCROLLER CODE ENDS HERE....
};
this.callback = function (){
this.currentSection = (this.currentSection != null)
? (this.currentSection + 1) % this.numSections
: 0
;
$("#content .section").removeClass("active");
$("#content .section:eq(" + this.currentSection + ")").addClass("active");
this.transition();
}
this.run = function(){
this.callback();
};
}
manager = new SectionManager();
manager.run();
Notably also, I had to overwrite the $jScroller.scroll function to contain an asynchronous callback to fire when the end is reached: This will trigger the manager's callback function and shift the scrolling functionality to the next section.
Edit: See the jsfiddle for details
It sounds like you could use Scrollorama. You can pin and unpin stuff depending on scroll, while other elements (content) scroll normally.