Fix header after scrolling Angular 4 - javascript

I am having issue fixing the header after scrolling, I tried a lot of stuff but can't get it to work. I checked this thread but it doesnt work for me: Angular 4 #HostListener Window scroll event strangely does not work in Firefox . This is my component structure:
Layout
Steps
Routes
Inside steps is my header which I want to fix, after scrolling for 50px. Inside Layout is some other content like a div with logo background (above the content of steps).
This is what I tried inside Steps.ts
#HostListener('window:scroll', [])
onWindowScroll() {
const number = window.scrollY;
if (number > 40) {
this.fixed = true;
} else if (this.fixed && number < 10) {
this.fixed = false;
}
}
but the problem is that scroll is not triggering at all. I found examples
where scroll logs the event, but for me it doesn't work (I tried with $event as well). Anyone has a solution?

Found a solution. On my layout component I put a function
(scroll)="onWindowScroll($event)"
and in layout component i used:
#HostListener('window:scroll', ['$event'])
onWindowScroll($event) {
const number = $event.target.scrollTop;
if (number > 40) {
this.fixed = true;
} else if (this.fixed && number < 10) {
this.fixed = false;
}
}
I removed Steps component since I didnt need it anymore, all the content is inside layout now.

In Angular 5+ it works a little differently:
const number = $event.target.scrollingElement.scrollTop || $event.target.documentElement.scrollTop;

Since some people come via Google to this question:
I'm quite a fan of moving logic like this into something re-useable. For Angular this would mean a directive. Therefore as I run into this issue myself I created a library from my code that at least has some tests and support across many browsers. So feel free to use this tested piece of code instead of polluting your components with more code.
https://w11k.github.io/angular-sticky-things/
With the code I see in the answer I did run into some issues. In another SO I found this solution. It is crucial to determine the offsetY of the header element correctly.
// Thanks to https://stanko.github.io/javascript-get-element-offset/
function getPosition(el) {
let top = 0;
let left = 0;
let element = el;
// Loop through the DOM tree
// and add it's parent's offset to get page offset
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return {
y: top,
x: left,
};

Related

2 Lines Causing thousands of Java Script Errors

On this page: https://www.airsyspro.com There is a script for a stick header that seems to have an infinite loop that is causing thousands of errors in the console. Any ideas on how to fix this issue?
You'll see in the console that it appears on lines 2216 and 2245. JS is not my expertise, thanks in advance.
if ($('.hpg-sticky-bar').length) {
$('.hpg-sticky-bar').addClass('original').clone().insertAfter('.hpg-sticky-bar').addClass('cloned').css('position','fixed').css('top','0').css('margin-top','0').css('z-index','500').removeClass('original').hide();
scrollIntervalID = setInterval(stickIt, 10);
function stickIt() {
var orgElementPos = $('.original').offset();
orgElementTop = orgElementPos.top;
if ($(window).scrollTop() >= (orgElementTop)) {
// scrolled past the original position; now only show the cloned, sticky element.
// Cloned element should always have same left position and width as original element.
orgElement = $('.original');
coordsOrgElement = orgElement.offset();
leftOrgElement = coordsOrgElement.left;
widthOrgElement = orgElement.css('width');
$('.cloned').css('left',leftOrgElement+'px').css('top',0).css('width',widthOrgElement).show();
$('.original').css('visibility','hidden');
} else {
// not scrolled past the menu; only show the original menu.
$('.cloned').hide();
$('.original').css('visibility','visible');
}
}
}
It can be as simple as the example here above
I think that the problem is this:
var orgElementPos = $('.original1').offset();
There is not a single element with class 'original'.
The var orgElementPos is null.
Also check this:
$('.msb-sticky-bar').addClass('original1')
the class msb-sticky-bar is misssing.

Replacing Bootstrap Dropdown with Dropup (Different activity on two near identical implementations)

I'm working on a project over at github pages, which I replace a bootstrap .dropdown with .dropup if the div's overflow-y: scroll will cause the dropdown menu to be cutoff / overflow. You can see the function working properly at this jsfiddle. Notice if you click on the ellipsis icon to the right on the top rows, it will drop down, if you click on the icon on the bottom rows, it will drop up.
Now, my actual implementation (github page), the code is exactly the same (below), but it wants to replace all .dropdown classes with .dropup when opened, including the top-most row which gets cut off, seen in the photo below.
I've been struggling with this for a week and can't quite figure it out. I've tried a few different things that I thought fixed it but ended up just being a hack and didn't work on mobile, or replaced some but not all etc.
Here is the Javascript / jQuery I'm using, which can be seen in the jsfiddle and my github source here.
$(document).on("shown.bs.dropdown", ".dropdown", function () {
// calculate the required sizes, spaces
var $ul = $(this).children(".dropdown-menu");
var $button = $(this).children(".song-menu");
var ulOffset = $ul.offset();
// how much space would be left on the top if the dropdown opened that direction
var spaceUp = (ulOffset.top - $button.height() - $ul.height()) - $('#playlist').scrollTop();
// how much space is left at the bottom
var spaceDown = $('#playlist').scrollTop() + $('#playlist').height() - ((ulOffset.top + 10) + $ul.height());
// switch to dropup only if there is no space at the bottom AND there is space at the top, or there isn't either but it would be still better fit
if (spaceDown < 0 && (spaceUp >= 0 || spaceUp > spaceDown))
$(this).addClass("dropup");
}).on("hidden.bs.dropdown", ".dropdown", function() {
// always reset after close
$(this).removeClass("dropup");
});
Edit:
To clear up any confusion, here's an example of the behavior without my added .dropup function. jsfiddle Notice when you click the last menu item, it opens the menu but requires scrolling. I specifically want to remove the .dropdown class and add .dropup in this case, so no scrolling is required.
It took some basic math, but I managed to figure out what you desired to do. This code changes the bootstrap classes between dropup and dropdown depending on the room available for a normal dropdown.
I calculated this by detracting the height of the button, dropdownmenu and how far the button was scrolled down in the scrollContainer from the height of the scrollContainer. I got the value how much the div was scrolled down by using the buttons offset and detracting the offset from the scrollContainer.
Here is my jQuery (I selected the .playlist class because this was attached to your scrollContainer, but you should replace it by an id or select it by other means):
$(".dropdown, .dropup").click(function(){
var dropdownClassCheck = $(this).hasClass('dropdown');
var buttonOffset = $(this).offset().top;
var scrollboxOffset = $('.playlist').offset().top;
var buttonHeight = $(this).height();
var scrollBoxHeight = $('.playlist').height();
var dropDownButtonHeight = $(this).children('ul').height();
dropdownSpaceCheck = scrollBoxHeight>buttonOffset-scrollboxOffset+buttonHeight+dropDownButtonHeight;
if(dropdownClassCheck && !dropdownSpaceCheck){
$(this).removeClass('dropdown').addClass('dropup');
}
else if(!dropdownClassCheck && dropdownSpaceCheck){
$(this).removeClass('dropup').addClass('dropdown');
}
});
A working JSFiddle
Let me know if there are parts of the code that could be improved/done easier or if there are any problems with my solution.
I have not thoroughly checked, but .scrollTop() is probably why the code fails when combined with other elements in the DOM, so here is a solution without it:
function checkHeights(){
// LOOP through each dropdown
$('.dropdown,.dropup').each(function(index,element){
var $dropDown = $(element),
$dropDownMenu = $dropDown.find('.dropdown-menu'),
dropDownTop = $dropDown.offset().top,
visibleHeight = $dropDown.height(),
hiddenHeight = $dropDownMenu.height(),
ddTop = dropDownTop - hiddenHeight,
ddBottom = dropDownTop + visibleHeight + hiddenHeight;
// LOOP through all parents
$dropDown.parents().each(function(ix,el){
var $el = $(el);
// CHECK if any of them have overflow property set
if( $el.css('overflow') !== 'visible' ){
var limitTop = $el.offset().top,
limitBottom = limitTop + $el.height();
// CHECK if parent is better fit when dropped upside
if( limitBottom < ddBottom && ( ddTop - limitTop ) > ( limitBottom - ddBottom ) )
$dropDown.removeClass('dropdown').addClass('dropup');
else
$dropDown.removeClass('dropup').addClass('dropdown');
// BREAK LOOP
return false;
}
});
});
}
$(document).ready(function() {
checkHeights();
$('.playlist').scroll(checkHeights);
});
JS Fiddle here.
This one does not require any class or id given to it except for dropdown,dropdown-menu, and dropup (all of which are Bootstrap defaults) and would work fine even if there are multiple playlists on page.
UPDATE
The code is modified and wrapped in a function in order to allow being called when scroll event fires.
I think that the problem it's that you have a big header, and the jsFiddle don't. So ulOffset.top it's always big, and spaceDown is always negative
Replace parent div.dropdown with div.dropup.

Force phonegap/cordova app to bounce when scrolling even with a single item

This question is similar to this one: Force uiscrollview to bounce scrolling even with a single item but the answer didn't solve my problem as I am using cordova, HTML 5 and Javascript to build my app.
My app uses iScroll to implement bounce effect on scrolling but it is not bouncing when the content fits the whole screen; it is not having the bounce effect when there is no need to scroll.
Help? Thanks!
I can't seem to find an alternative that lets me do this so I modified the core iscroll.js (v5.1.1) and added my own code inside iScroll.prototype.refresh:
this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0;
this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;
//---- my code starts here
// set default value to 0 if the maxscrolly is greater than 0
//and set hasVerticalScroll to true (always true)
this.maxScrollY = (this.maxScrollY > 0) ? 0) : this.maxScrollY;
this.hasVerticalScroll = true;
//---- my code ends here
if ( !this.hasHorizontalScroll ) {
this.maxScrollX = 0;
this.scrollerWidth = this.wrapperWidth;
}
if ( !this.hasVerticalScroll ) {
this.maxScrollY = 0;
this.scrollerHeight = this.wrapperHeight;
}
*this will enable the scroll bounce effect even if the page has no content.

Get divs within viewport inside a wrapper div

Is there a way to get elements which is:
Inside a div with overflow: scroll
Is in viewport
Just like the following picture, where active div (5,6,7,8,9) is orange, and the others is green (1-4 and >10) :
I just want the mousewheel event to add "active" class to div 5,6,7,8,9 (currently in viewport). View my JSFiddle
$('.wrapper').bind('mousewheel', function (e) {
//addClass 'active' here
});
You could do something like this. I would have re-factored it, but only to show the concept.
Firstly I would attach this to scroll event and not mousewheel. There are those among us that likes to use keyboard for scrolling, and you also have the case of dragging the scrollbar. ;) You also have the case of touch devices.
Note that with this I have set overflow:auto; on wrapper, thus no bottom scroll-bar.
With bottom scrollbar you would either have to live with it becoming tagged as in-view a tad to early, or tumble into the world of doing a cross-browser calculating of IE's clientHeight. But the code should hopefully be OK as a starter.
»»Fiddle««
function isView(wrp, elm)
{
var wrpH = $(wrp).height(),
elmH = $(elm).height(),
elmT = $(elm).offset().top;
return elmT >= 0 &&
elmT + elmH < wrpH;
}
$('.wrapper').bind('scroll', function (e) {
$('div.box').each(function(i, e) {
if (isView(".wrapper", this)) {
$(this).addClass('active');
} else {
$(this).removeClass('active');
}
});
});
Note that you should likely refactor in such a way that .wrapper height is only retrieved once per invocation, or if it is static, at page load etc.
Update; a modified version of isView(). Taking position of container into account. This time looking at dolphins in the pool.
»»Fiddle««
function isView(pool, dolphin) {
var poolT = pool.offset().top,
poolH = pool.height(),
dolpH = dolphin.height(),
dolpT = dolphin.offset().top - poolT;
return dolpT >= 0 && dolpT + dolpH <= poolH;
}

Two columned layout with multiple different height widgets, how to make widgets collapsable and keep flow of the page?

I have a main page that I'm working on and that is 2 columns with equal widths, with multiple widgets on the page. The page is responsive and the number of widgets is variable. At the moment I have a solution which works quite well and doesn't use a plugin like masonry or salvattore. The reason I've gone against using these plugins is that I don't want the page to be two rigid columns, I'd like the widgets to be able to flow and fit the available space.
This brings me onto my question - I'd like to be able to collapse any of these widgets and the other widgets should flow around and fit the space left. This is a prototype of what I have so far:
http://codepen.io/charge-valtech/pen/bzJfj
This is the jquery I've written:
function layoutWidgets() {
console.log('layout widgets');
if ($(".left-widget").css("float") == "left") {
$('.left-widget').each(function (index, value) {
var widgetPosition = $(this).position().left;
if (widgetPosition >= 30) {
$(this).removeClass('left-widget').addClass('right-widget');
}
});
$('.right-widget').each(function (index, value) {
var widgetPosition = $(this).position().left;
if (widgetPosition <= 30) {
$(this).removeClass('right-widget').addClass('left-widget');
}
});
}
}
layoutWidgets();
$(window).resize(layoutWidgets);
$('.collapse').click(function () {
$(this).closest('.widget').toggleClass('collapsed');
});
But for some reason if I put the layoutWidgets function inside the click function for collapsing the widgets, it doesn't work.
Any ideas?
Either put your code in a document.ready function or include it in the footer after everything is loaded. You can't attach a click event to an element that doesn't exist yet.
The way I've solved this is to do a for loop in my click function which should iterate through the widgets and run layoutWidgets:
$('.collapse-expand').click(function () {
$(this).closest('.dashboard-panel').toggleClass('collapsed');
var n = 7;
for (var i = 0; i < n; i++) {
layoutWidgets();
}
});

Categories