Long magnetic scroll page in Ionic 2 - javascript

Hey Guys i need your help please,
is started to working on an Ionic 2 App. My Navigation is not that complicated. I have one menu if i click one item another menu opens with a submenu and if i click on an item in the submenu a third page should render above it and this works really fine. Now the third activity should be a very long scrolling site with a lot of section (the sections are on top of each other). And every section should have a toolbar with one back button to go back to the submenu and two arrow keys for the previous or next section.
Here a small picture
now my problems:
how can i achieve the magnetic part? I think it like so: the Bar sits on the top of the page and above the content. When i scroll the content goes underneath and i can scroll to the end. When iam at the end everything should stop and when i pull further the next Section Bar jumps to the top of my site.
I hope you can help me thank you ;)

Plunker Demo
To make this work you need to:
Create a function that scrolls your scroll-content element to the top
Track the scroll position of scroll-content
Use *ngIf on your scroll to top button to conditionally show after scroll-content has reached a certain threshold.
Scroll to top function
I adapted this SO answer to apply to the scroll-content element
scrollToTop(scrollDuration) {
let scrollStep = -this.ionScroll.scrollTop / (scrollDuration / 15);
let scrollInterval = setInterval( () => {
if ( this.ionScroll.scrollTop != 0 ) {
this.ionScroll.scrollTop = this.ionScroll.scrollTop + scrollStep;
} else {
clearInterval(scrollInterval);
}
}, 15);
Track scroll-content position
This example uses the window height as the threshold for showing the scroll to top button like this:
this.ionScroll.addEventListener("scroll", () => {
if (this.ionScroll.scrollTop > window.innerHeight) {
this.showButton = true;
} else {
this.showButton = false;
}
});
Button Html
<button *ngIf="showButton" (click)="scrollToTop(1000)">Scroll Top</button>
Full component Typescript
import { NavController } from 'ionic-angular/index';
import { Component, OnInit, ElementRef } from "#angular/core";
#Component({
templateUrl:"home.html"
})
export class HomePage implements OnInit {
public ionScroll;
public showButton = false;
public contentData = [];
constructor(public myElement: ElementRef) {}
ngOnInit() {
// Ionic scroll element
this.ionScroll = this.myElement.nativeElement.children[1].firstChild;
// On scroll function
this.ionScroll.addEventListener("scroll", () => {
if (this.ionScroll.scrollTop > window.innerHeight) {
this.showButton = true;
} else {
this.showButton = false;
}
});
// Content data
for (let i = 0; i < 301; i++) {
this.contentData.push(i);
}
}
// Scroll to top function
// Adapted from https://stackoverflow.com/a/24559613/5357459
scrollToTop(scrollDuration) {
let scrollStep = -this.ionScroll.scrollTop / (scrollDuration / 15);
let scrollInterval = setInterval( () => {
if ( this.ionScroll.scrollTop != 0 ) {
this.ionScroll.scrollTop = this.ionScroll.scrollTop + scrollStep;
} else {
clearInterval(scrollInterval);
}
}, 15);
}
}
Full component Html
<ion-navbar primary *navbar>
<ion-title>
Ionic 2
</ion-title>
<button *ngIf="showButton" (click)="scrollToTop(1000)">Scroll Top</button>
</ion-navbar>
<ion-content class="has-header" #testElement>
<div padding style="text-align: center;">
<h1>Ionic 2 Test</h1>
<div *ngFor="let item of contentData">
test content-{{item}}
</div>
</div>
</ion-content>

Related

Change fixed logo color on scroll depending of current section class?

I am trying to change the color of my fixed SVG logo based on the current section you scroll. For instance, if you scroll inside a section with a class "intro" or "projects" I want to change the logo color with a CSS class.
So, on entering the section and at END of the section scrolling from top and bottom to remove and add the previous class.
Basically, I have an array of all sections looping through them with an EventListener scroll which should toggle the logo class on a scroll and IF inside a section or not.
Can someone help me out?
My code:
(() => {
const logo = document.querySelector('.logo');
const sections = document.querySelectorAll('section');
if(!sections.length) {
return;
}
const onPageScroll = () => {
sections.forEach(section => {
console.log(section);
});
}
window.addEventListener('scroll', onPageScroll);
})();
I believe one of these pens should help you. What you're trying to do is a scroll spy.
vanilla JS scrollSpy codePen 1
vanilla JS scrollSpy codePen with smooth scroll
From the second pen:
const spyScrolling = ( ) => {
const sections = document.querySelectorAll( '.hero-bg' );
window.onscroll = ( ) => {
const scrollPos = document.documentElement.scrollTop || document.body.scrollTop;
for ( let s in sections )
if ( sections.hasOwnProperty( s ) && sections[ s ].offsetTop <= scrollPos ) {
const id = sections[ s ].id;
document.querySelector( '.active' ).classList.remove( 'active' );
document.querySelector( `a[href*=${ id }]` ).parentNode.classList.add( 'active' );
}
}
}
There are plenty of other examples available out there, you just have to know the right keywords.

Need to click element twice

I have seen most of the posts about this issue but none seems to work for me. (I am new to React)
Basically I have this js function:
export function toggle() {
"use strict"; // Start of use strict
// Configure tooltips for collapsed side navigation
$('.navbar-sidenav [data-toggle="tooltip"]').tooltip({
template: '<div class="tooltip navbar-sidenav-tooltip" role="tooltip" style="pointer-events:
none;
"><div class="
arrow "></div><div class="
tooltip - inner "></div></div>'
})
// Toggle the side navigation
$("#sidenavToggler").click(function(e) {
e.preventDefault();
$("body").toggleClass("sidenav-toggled");
$(".navbar-sidenav .nav-link-collapse").addClass("collapsed");
$(".navbar-sidenav .sidenav-second-level, .navbar-sidenav .sidenav-third-
level ").removeClass("
show ");
});
// Force the toggled class to be removed when a collapsible nav link is clicked
$(".navbar-sidenav .nav-link-collapse").click(function(e) {
e.preventDefault();
$("body").removeClass("sidenav-toggled");
});
// Prevent the content wrapper from scrolling when the fixed side navigation hovered over
$('body.fixed-nav .navbar-sidenav, body.fixed-nav .sidenav-toggler, body.fixed-nav .navbar-
collapse ').on('
mousewheel DOMMouseScroll ', function (e) {
var e0 = e.originalEvent,
delta = e0.wheelDelta || -e0.detail; this.scrollTop += (delta < 0 ? 1 : -1) * 30; e.preventDefault();
});
})(jQuery); // End of use strict
which is used to make a sidebar smaller. The point is that I need to click twice to make the sidebar smaller. I am doing it like this. Here I receive the function:
const Border = ({ isClicked }) => (
And then calling it from the element:
<a onClick="{isClicked}" className="nav-link text-center" id="sidenavToggler">
<i className="fa fa-fw fa-angle-left"></i>
</a>
This is the class that calls the const:
class CitasPage extends React.Component {
handleToggleCreate = () => {
toggle();
this.setState({});
};
render() {
return (
<div className="content-wrapper">
<Border isClicked={this.handleToggleCreate}></Border>
<Citas />
</div>
);
}
}
export default CitasPage;

Focused Highlighting of list items based on mouse scroll increments

I have a drop down menu where I need list items to be highlighted based on the mouse scroll increments. When the user hovers over the list and scrolls 1x up or down, the item immediately above or below should be highlighted. This involves getting the deltaY of the mousescroll event I believe. The problem is that this deltaY value is super sensitive. So when there's even a slight scroll up gesture, it logs it as something crazy like 7x/8x. How should I approach handling this functionality.
This is what I currently have (but feel free to suggest something completely different if it'll work better):
***(also, (event.target.id[event.target.id - 1]) this isn't working. what am I doing wrong?)
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
highlightListItem(event: any, scrollUp?: boolean, scrollDown?: boolean): void {
this.highlightTimer = setTimeout(() => {
this.someService.highlightListItem(event.target.id);
}, 450);
if (scrollUp) {
this.highlightListItem = setTimeout(() => {
this.someService.highlightListItem(event.target.id[event.target.id - 1]);
}, 450)
} else if (scrollDown) {
this.highlightTimer = setTimeout(() => {
this.someService.highlightTimer(event.target.id[event.target.id - 1]);
}, 450);
}
scrollEvent(event: any): void {
console.log(event.deltaY * 100);
if (event.deltaY < 0) {
this.highlightListItem(event, false, true);
} else if (event.deltaY > 0) {
this.highlightListItem(event, true, false);
}
}
HTML:
<mat-card class="some-card" *ngFor="let property of properties; let i = index;" id="segment{{i}}"
(mousewheel)="scrollEvent($event)">
<mat-card-content class="some-table">
<div class="index">{{i + 1 }}</div>
<div class="someClass">
<div>{{property.field}}</div>
</div>
</mat-card-content>
</mat-card>

Scroll event not working in framework7 - Vue

Am currently using framework7 and I have this problem wherein I need to get a button floating once the user pass scrolling a specific element.
But for some reason am not able to make the scroll event work. Even used a native event listener but still no luck.
Here is my code. In my component:
export default {
methods: {
handleScroll(event) {
alert('should work')
}
},
created() {
window.addEventListener('scroll', this.handleScroll);
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll);
},
mounted() {
window.addEventListener('scroll', this.handleScroll)
this.handleScroll;
var element = document.querySelector(".similar-adventures");
var top = element.offsetTop;
window.scrollTo(0, top);
}
}
And here is my native event listener code:
window.addEventListener(‘scroll’, function(e){
// Get the new Value
newValue = window.pageYOffset;
//Subtract the two and conclude
if(oldValue - newValue < 0){
console.log(“Up”);
} else if(oldValue - newValue > 0){
console.log(“Down”);
}
// Update the old value
oldValue = newValue;
});
I know this is old now but i will answer for future reference, so i think the problem here is that the window is not actually scrolling as framework7 uses pages/views.
In vue the renders to 2 divs like so..
<f7-page>
<div slot="fixed">Fixed element</div>
<p>Page content goes here</p>
</f7-page>
<!-- Renders to: -->
<div class="page">
<div>Fixed element</div>
<div class="page-content">
<p>Page content goes here</p>
</div>
</div>
i found that its the page-content class that you want to put the eventListenter on best way to do this is Dom7 like so...
let page = $$('.page-content')
page.on('scroll', () => {
console.log(page.scrollTop()) // will show page top position
page.scrollTop(0) // will scroll to top
})
//if you have multiple pages
let page = $$('.page-content')
let home = $$(page[0])
let about = $$(page[1])
page.on('scroll', () => {
console.log(home.scrollTop()) //home page top position
console.log(about.scrollTop()) //about page top position
})
//more options
page.scrollTop(position, duration, callback)
page.scrollTo(left, top, duration, callback)
just remember to import $$ from 'Dom7'
This code retrieves all the pages from the f7 component in an array
let pages = document.querySelectorAll('.page-content');
Then to make a page scrollable, select the respective index and do:
pages[0].addEventListener('scroll', function () { console.log('is scrolling...') } );
For the same code but in a more beautiful way as we don't want to specify the page by index:
add an id to your f7-page tag
<f7-page name="whatever" id='myPage'>
then do this code for example in mounted:
let f7page = document.getElementById('myPage');
let scrollableDiv = f7page.querySelector('.page-content');
scrollableDiv.addEventListener('scroll', function () { console.log('is scrolling...') } );
special thanks to BiscuitmanZ's comment for finding the underlying issue

Dynamically change angular 6 sidebar's width and mode

I'm developing an app with Angular 6, where I want to have a responsive sidebar, and decided to use angular material's sidebar module. The problem is: I want for the sidebar to have a collapsed mode that is not entirely hidden, which isn't how angular sidebar's work, so I did something similar to this (Medium.com).
Now, the behaviour I want for my sidebar: I want it to be able to be collapsed or expanded while the resoltion of the screen is higher than 1366 of width, and have mode set to 'side', and when the resolution is smaller than that, I want it set to 'side' if collapsed, or 'over' if expanded.
How I did that:
<div id="complete-container">
<mat-sidenav-container>
<mat-sidenav opened="True" [style.width]="isExpanded ? expandedWidth : collapsedWidth" [mode]="mode">
<app-sidebar *ngIf="isExpanded" [version]="version" [isExpanded]="isExpanded" (isExpandedEvent)="collapse($event)"></app-sidebar>
<app-collapsed-sidebar *ngIf="!isExpanded" [version]="version" [isExpanded]="isExpanded" (isExpandedEvent)="collapse($event)"></app-collapsed-sidebar>
</mat-sidenav>
<mat-sidenav-content [style.margin-left]="isExpanded ? expandedWidth : collapsedWidth">
<p>Main content</p>
</mat-sidenav-content>
</mat-sidenav-container>
</div>
And, on my component:
export class AppComponent {
mobileQuery: MediaQueryList;
private _mobileQueryListener: () => void;
isExpanded: boolean;
expandedWidth = '220px';
collapsedWidth = '80px';
mode: string;
constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher) {
this.mobileQuery = media.matchMedia('(max-width: 1366px)');
this._mobileQueryListener = () => changeDetectorRef.detectChanges();
this.mobileQuery.addListener(this._mobileQueryListener);
}
ngOnDestroy(): void {
this.mobileQuery.removeListener(this._mobileQueryListener);
}
ngOnInit(): void {
if (this.mobileQuery.matches) {
this.isExpanded = false;
} else {
this.isExpanded = true;
}
this.mode = 'side';
}
collapse(change: boolean) {
this.isExpanded = !this.isExpanded;
if (this.mobileQuery.matches && this.isExpanded) {
this.mode = 'over';
} else {
this.mode = 'side';
}
}
}
collapse() is the method that gets called when you click the button to expand/collapse the sidebar.
Now, what is the problem with that? When my resolution width is smaller than 1366, and the sidebar goes from expanded to collapsed, the main content gets displaced, like if the sidebar was set to mode 'side' (which is correct), but it's width was the one while it is expanded, and not collapsed.
Images of how it is behaving:
Fullscreen expanded (working ok)
Fullscreen collapsed (working ok)
Small screen expanded (working ok)
Small screen collapsed (weird space between sidebar and main content)
Does anyone know why is it behaving like this? Or how should it be done to get the expected result?
Thanks in advance
Had a similar problem, one fix could be enabling [autosize]="true" on <mat-sidenav-container>
You also can look at: https://github.com/angular/material2/issues/6743
It provides some other hacky workarounds.

Categories