Bootstrap affix navbar for single page with scrollspy and page anchors - single-page-application

This is for a single page, with a navbar that links to local anchors only.
The navbar comes after a header, but sticks to top when scrolling down.
You can see how it works on github pages
But I've got two offset problems with link/anchors:
as long as you don't scroll, the anchors are offset and masked by the navbar.
once the navbar is affixed, the following links work as intended but not the first one.
A body margin breaks the layout as it prevents the header from beginning right at the top:
body {
margin-top: 65px;
}
I've tried without success to play with margin/padding for the sections:
section {
padding-top: 65px;
margin-top: -65px;
}
Here are the html and css
Any idea how to fix that?
Can it be solved with pure css?
Or do I need some js fix to account for the affix?

I think your problem has only to do with the affix. I found a problem in 3 situations:
no scroll and clicking a link
click the first link
scoll, click the first link and click an other link.
In this three situation you click from an position where you affix is not applied to a position where your affix has been applied.
What happens your click scrolls the target anchor to the top of the page and applies the affix (set navbar's position to fixed) after this. Result the navbar overlaps the content.
I don't think you could fix this with css only. I think your solution of adding a margin / padding to the section will be right, but you will have to apply the margin after the affix.
I tried something like:
var tmp = $.fn.affix.Constructor.prototype.checkPosition;
var i = 0;
var correct = false
$.fn.affix.Constructor.prototype.checkPosition = function () {
$('#content').css('margin-top','0');
tmp.call(this);
if(i%2!=0 && $(window).scrollTop()<443){correct=true}
if(i%2==0 && correct){$('#content').css('margin-top','83px').trigger('create'); correct=false}
i++;
}
This feels to complex and also only seems to work on firefox now.
update
I think i could fix your problem by overwritting the complete affix checkPosition function:
$.fn.affix.Constructor.prototype.checkPosition = function ()
{
if (!this.$element.is(':visible')) return
var scrollHeight = $(document).height()
var scrollTop = this.$window.scrollTop()
var position = this.$element.offset()
var offset = this.options.offset
var offsetTop = offset.top
var offsetBottom = offset.bottom
if(scrollTop==378)
{
this.$window.scrollTop('463');
scrollTop==463;
}
if (typeof offset != 'object') offsetBottom = offsetTop = offset
if (typeof offsetTop == 'function') offsetTop = offset.top()
if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
console.log(scrollTop + ':' + offsetTop);
if(scrollTop > offsetTop) {$('#content').css('margin-top','83px'); console.log('margin') }
else{$('#content').css('margin-top','0');}
if (this.affixed === affix) return
if (this.unpin) this.$element.css('top', '')
this.affixed = affix
this.unpin = affix == 'bottom' ? position.top - scrollTop : null
this.$element.removeClass('affix affix-top affix-bottom').addClass('affix' + (affix ? '-' + affix : ''))
if (affix == 'bottom') {
this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })
}
}
Some values are hard coded (now) so this function only will work for your example on github pages.
Demo: http://bootply.com/81336
On github pages you use "old" versions of jQuery and Bootstrap. You don't need to set an offset for the scrollspy. You don't have to call $('#navbar').scrollspy(); also cause you already set the scrollspy with data attributes.
See also: https://github.com/twbs/bootstrap/issues/10670
remove this hardcode values
When clicking an internal link (start with #{id}) the anchor with id={id} will be scrolled to the top of the viewport.
In this case there will be a fixed navbar (affix) so the anchor should scroll to the top minus the height of the navbar.
The height of the navbar will be 85px (63 pixels of the brand image + 2 pixels of the border + the margin-bottom of 20 px of the .navbarheader)
This value will be used here:
if(scrollTop > offsetTop) {$('#content').css('margin-top','83px'); console.log('margin') }
else{$('#content').css('margin-top','0');}
I have used 83 (may look better?).
So the 83 can be replaced with: var navbarheight = $('#nav').innerHeight()
Then we have these:
if(scrollTop==378)
{
this.$window.scrollTop('463');
scrollTop==463;//typo?? make no sense
}
The (first) link scrolls the anchor to the top where the affix is not
applied yet (below data-offset-top="443") the height of your fixed
navbar is not used in calculacting so this point will be 443 - 85
(navbarheight) = 378. This code could be replace with.
if(scrollTop==(443-navbarheight))
{
this.$window.scrollTop(scrollTop+navbarheight);
}
Note 443 now still will be hardcoded. It is also hardcoded in your
html with affix-top.
Watch out Replacing the values with the above won't work. The
situation between (af)fixed and not will change for every scroll
action. The part if(scrollTop==378) is a trick not a solution. It
solves the situation for scrollheight < data-offset-top. We could not
apply the whole range, case in that case the user can't never scroll
back to the top (this.$window.scrollTop scrolls him back again and again).
Also the calculation of navbarheight will be tricky. When the navbar
is fixed $('#nav').innerHeight() / height will return 85 (including
the margin). In the absolute position this will be 65.

Related

Pure CSS parallaxing (not fixed) background for single row in layout

I'm wondering if it's possible to use only CSS to create a parallax scrolling background that meets the following specifications.
It works on an element that sits inside an otherwise static layout (i.e. my whole page layout isn't a group of parallaxing items)
The background isn't entirely fixed in place; it moves, just not as fast as the rest of the page.
I've looked up tons of tutorials for parallaxing backgrounds, and have found some seemingly great tutorials, but they all have one of the following problems.
They rely on the whole page being a parallax group so that you're actually scrolling over a container via an "overflow: auto" specification
The background is totally fixed in place
they use JavaScript.
Sooo, I can accomplish what I want with JavaScript fairly easily. Here's a full working example on JSFiddle that you can try out.
CSS
.parallax-row {
background-image: url(http://lorempixel.com/output/nature-q-c-781-324-3.jpg);
background-size: auto 150%;
}
JavaScript
/**
* Update the parallaxing background img to partially scroll
*/
jQuery(document).ready(function($) {
$(window).on('scroll', function() {
$('.parallax-row').each(function(index, el) {
var $el = $(el);
var fromTop = $el.offset().top + ($el.outerHeight() / 2) - $(window).scrollTop();
var windowHeight = $(window).height();
var percent = (fromTop * 100 / windowHeight);
$el.css('background-position', '0 ' + percent + '%');
});
});
});
Is it possible to accomplish that same effect with just CSS?

Issue with Mega Menu on Magento

I have 1 Issue with the megamenu on site link
screenshot
Sometimes the menu drop down shifts to right . This happens on both chrome and firefox .
It is not a regular issue it only happens sometimes.
what i assume it may be a css issue becouse when ever it happens and i inspect element with a chrome css extension ..automatically the box shifts to the correct place without refreshing
the megamenu code is attached
$sns_jq(function($){
var wrap = $('#sns_menu');
var container = $('#sns_menu .container');
$('.sns-megamenu-wrap').find('li').each(function(){
var menucontent = $(this).find(".mega-content-wrap:first");
var li = $(this);
if( (container.outerWidth() + container.offset().left) < (li.offset().left + menucontent.outerWidth()) ){
menucontent.css({"left": (container.outerWidth() - menucontent.outerWidth() )+"px"});
}
});
$(window).resize(function(){
setTimeout(function(){
$('.sns-megamenu-wrap').find('li').each(function(){
var menucontent = $(this).find(".mega-content-wrap:first");
var li = $(this);
if( (container.outerWidth() + container.offset().left) < (li.offset().left + menucontent.outerWidth()) ){
menucontent.css({"left": (container.outerWidth() - menucontent.outerWidth() )+"px"});
}
});
}, 200);
});
});
The theme is sns korion
I checked out your css. You are handling your visibility toggle with visibility and opacity. Pick one. In this particular case, I would pick opacity because of the transitions you are running.
Also, your transform css with scale is placing the dropdowns in a different place and using scale to place them in the right place by size. But, contradictory to this technique, you set the transition to none afterwards. This is all a back forth positioning that messes up with the display if the keyframes stop unexpectedly.
SO delete all your transitions in line 6599 in your theme-light-green.css and all your transitions and transforms in line 6462 same stylesheet.
Also remove the visibility in both lines and the opacity in 6599. (you already have it in 6462.
Good luck

How to override position of primefaces OneMenu?

How to override primefaces OneMenu in order to see it over captcha, ie below? My selectOneMenu have no any changes.
My guess is that the menu panel doesn't have enough space to fit in the lower part, instead it's positioned above, as the aligning of the panel is being set by javascript (PrimeFaces.widget.SelectOneMenu.alignPanel), using the jQuery UI .position() method which allows you to position an element relative to the window, document, another element, or the cursor/mouse, without worrying about offset parents, and the default value for collision attribute is flip (In PrimeFaces 5 it's flipfit) resulting the positioned element overflows the window in some direction, or to move it to an alternative position.
In this case you could implement one of these three solutions:
extend the space on the lower part, maybe adding margin to the
captcha, in this way the panel would fit in bottom.
OR change the hight of the panel
<p:selectOneMenu height="100" >
Making it a bit shorter so it can fit.
OR you can override the PrimeFaces.widget.SelectOneMenu.alignPanel function
to set the collision attribute to none, in the position function:
PrimeFaces 5
PrimeFaces.widget.SelectOneMenu.prototype.alignPanel = function() {
if(this.panel.parent().is(this.jq)) {
this.panel.css({
left: 0,
top: this.jq.innerHeight()
});
}
else {
this.panel.css({left:'', top:''}).position({
my: 'left top'
,at: 'left bottom'
,of: this.jq
,collision: 'none' // changing from flipfit to none
});
}
}
PrimeFaces 4
PrimeFaces.widget.SelectOneMenu.prototype.alignPanel = function() {
var fixedPosition = this.panel.css('position') == 'fixed',
win = $(window),
positionOffset = fixedPosition ? '-' + win.scrollLeft() + ' -' + win.scrollTop() : null;
this.panel.css({left:'', top:''}).position({
my: 'left top'
,at: 'left bottom'
,of: this.jq
,offset : positionOffset
,collision: 'none' // changing from default flip to none
});
}
Of course you should call it in the document.ready, and when you update the component.
I don't recommend this approach too much, but sometimes it's the only solution.
Hope this helps.
For necessary SelectOneMenu add style top find an optimal value and apply it. For me it is:
#registrationForm\:facultyList_panel {
top: 413px !important;
}
UPDATE 09.07: It does not helps for another screen resolution. The question is still relevant.

Ordering divs with javascript

I have something like this:
http://s9.postimg.org/wwizuwnq7/Untitled_1.png
And if you see, the divs (Where I marked in green) have a space of some pixel.
And i want if there is a 0-20 pixel space between divs, to order them like this:
http://s23.postimg.org/ky2htcpt7/image.png
So, i started to do this on javascript and i dont know to to continue..
var position = new Array();
$(".post").each(function(){
position[$(this).attr("id")] = $(this).offset().top - $(window).scrollTop();
});
now i have all the position of all the divs, and now i need to check where divs have a space of 0 - 20 pixel, and then i want to take down the higher block.
I not sure if this is the good way, and if now, i need another idea..
Thanks!
I managed to find a method!
var position = new Array();
$(".hblocks").each(function(){
position[$(this).attr("id")] = $(this).offset().top;
});
$.each(position, function(key, value) {
$.each(position, function(key2, value2) {
var space = value2 - value;
if (space <= 20 && space >= -20 && space != 0)
{
var finalSpace = Math.max(value, value2);
var spaceplus = space + 28;
if (finalSpace != value)
{
$("#" + key).css("margin-top",spaceplus + "px");
}
else
{
$("#" + key2).css("margin-top",spaceplus + "px");
}
}
});
});
You can do this by adding a container div around the bottom 2 blocks. That way they will always be in line, regardless of the height of either of the top two blocks. You should try not to use javascript for styling. CSS is very powerful.
Here's a fiddle: http://jsfiddle.net/kVn7x/
HTML:
<div>
<div style='height:100px;'></div>
<div style='height:200px;'></div>
</div>
<div style='clear:left'>
<div style='height:80px;'></div>
<div style='height:80px;'></div>
</div>
CSS:
div div{background:red; width:150px; display:inline-block; margin:5px; float:left; clear:none}
Can't you simply add a bottom margin to the selected element in CSS?
#element {
margin-bottom: 20px;
}
Your answer would be some king of javascript+css coding to verify height of elements .. work on em then re-arrange them.
Stop trying to figure out by yourself, try using Masonry or jQueryEqualHeight explained on CSSTrick.
What is Masonry?
Masonry is a JavaScript grid layout library. It works by placing
elements in optimal position based on available vertical space, sort
of like a mason fitting stones in a wall. You’ve probably seen it in
use all over the Internet.
This is untested but something like this should work after your code...
The idea is to continuously add 1pixel to the top margin of the problematic div until the difference between the two divs is 20px
while(position['div1'] - position['div2'] <20){
$('#div2').animate({marginTop: '+=1px'}, 0);​​​​​​​​​​​​​​​​​
}
If you want to show them directly in line as in your picture, it's even easier:
var diff = position['div1'] - position['div2']
if(diff < 20){
$('#div2').animate({marginTop: '+=' + diff + 'px'}, 0);​​​​​​​​​​​​​​​​​
}

onclick does not work with some scrollTop values

I encountered a strange problem, maybe someone can help.
There are multiple img on the page, each one is defined as:
<td><img src="img1" onmouseout="Hide(\'div1\');"></td>
When user clicks on the image, the hidden div should appear in the middle of the page and disappear on the mouseout.
This is hidden div:
<div id="div1" style="width:400px;height:220px;padding:8px;position:absolute;display:none;border:6px solid #CC6600;background-color:#FFDAB4">
Everything works fine till I start scrolling up and down. At some specific positions and at the end of scroll the hidden div is not appearing. Does anyone understand why?
Here is javascript:
function Show (div)
{
var winW=630,winH=460,t,l;
if (document.body && document.body.offsetWidth) {
winW = document.body.offsetWidth;
winH = document.body.offsetHeight;
}
if (document.compatMode=='CSS1Compat' &&
document.documentElement &&
document.documentElement.offsetWidth ) {
winW = document.documentElement.offsetWidth;
winH = document.documentElement.offsetHeight;
}
if (window.innerWidth && window.innerHeight) {
winW = window.innerWidth;
winH = window.innerHeight;
}
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
t=winH/2-240/2+scrollTop;
l=winW/2-400/2+scrollLeft;
document.getElementById(div).style.top=t+"px";
document.getElementById(div).style.left=l+"px";
document.getElementById(div).style.display="block";
}
function Hide(div)
{
document.getElementById(div).style.display="none";
}
I just realized that it is not scrollTop problem.
Anchor inside div does not occupy whole div, only lower portion. Strange that cursor changes on the whole img, onclick is called on the whole img, but hidden div shows only when clicking on the lower portion.
I tried style="display:block" without any success. Out of desperation, I put style="opacity:0.9" and it does work! The only problem is that hidden div shows behind img (like having z-index lower than img). I am not using z-index since hidden div is position:absolute.
* Still looking for some smart person!!!
Try jQuery solution
$(function() {
$('body').scrollTop(0);
});
Meanwhile if you want a JavaScript solution try
document.body.scrollTop = document.documentElement.scrollTop = 0;

Resources