1. Tham số truyền vào scrollspy
[required] scrollOn: string = 'currentElement' | 'body' => bắt sự kiện scroll trên div chứa directive, hoặc body. Hiện tại support trên 'body'
[required] scrollSpySectionId: id của thẻ chứa nội dung scroll ( không phụ thuộc vào tham số scrollOn )
[required] tagIDList: danh sách id của section. Dựa vào danh sách này để navbar check đã scroll đế item nào.
Ex: tagIDList = [ 'id1', 'id2' ] or [[ 'tab1-id1', 'tab1-id1id2' ],[ 'tab2-id1id1', 'tab2-id2' ] ]
Gom các id thành 1 mảng hoặc tách thành các mảng của tab
Event click sidebar-menu: scrollSpyService.scrollTo('section1')": scroll xuống thẻ có id = 'section1'
Note:
Để giải quyết vấn đề khi scroll xuống đáy, nhưng navbar vẫn chưa check được sự kiện. Do con trỏ đã chạm đáy, nhưng phần tử vẫn chưa chạm top.
Cần thêm phần footer dài để bù vào khoảng trống bên dưới.
<div style="min-height: 100vh;"
id="scrollspy-section" [scrollSpySectionId]="'scrollspy-section'"
[scrollOn]="'body'" scrollSpy
[tagIDList]="['section1', 'section2', 'section3', 'section4']"
(sectionChange)="scrollSpyService.onSectionChange($event)"
>
<div class="row scrollspy">
<div class="col-sm-12 col-md-12 col-lg-9 main-content">
<div class="page-header-text">
Scrollspy + Default page
</div>
<hr>
<br>
<div id="section1">
<div class="title-text">1. Section1</div>
<div>Section1 Content</div>
</div>
<div id="section2">
<div class="title-text">2. Section2</div>
<div>Section2 Content</div>
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-3 position-relative">
<div class="position-fixed right-menu">
<div class="header-sidebar">
CONTENT
</div>
<div (click)="scrollSpyService.scrollTo('section1')" [ngClass]="{'right-menu-item': true, 'active': scrollSpyService.currentSection==='section1'}" >
Section1
</div>
<div (click)="scrollSpyService.scrollTo('section2')" [ngClass]="{'right-menu-item': true, 'active': scrollSpyService.currentSection==='section2'}" >
Section2
</div>
</div>
</div>
</div>
</div>
import { ScrollSpyService } from 'path to -> scroll-spy.service';
import { SetHeaderService } from 'path to -> set-header.service';
import { Component, OnInit } from '@angular/core';
export class ComponentName implements OnInit {
constructor(
public scrollSpyService: ScrollSpyService,
) {
}
ngOnInit() {
// Sử dụng khi có fixed header hoặc có tab. Khi load lại page sẽ đưa về tab mặc định.
this.scrollSpyService.setCurrentTabIndex(0);
}
setHeader(){
this.setHeaderService.setHeader(
{
header: 'Scrollspy + Default page',
description: 'Scrollspy + Default page'
}
);
}
}
import { ScrollSpyModule } from '../../shared/services/scroll-spy/scroll-spy.module';
imports: [
ScrollSpyModule
]
import { Directive, Input, EventEmitter, Output, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[scrollSpy]'
})
export class ScrollSpyDirective {
/*
params:
[required] scrollOn: string = 'currentElement' | 'body' => bắt sự kiện scroll trên div chứa directive, hoặc body. Hiện tại support trên body
[required] scrollSpySectionId: id của thẻ chứa nội dung scroll ( không phụ thuộc vào tham số scrollOn )
[required] tagIDList: danh sách id của section. Dựa vào danh sách này để check đã scroll đế item nào
*/
@Input() public scrollOn: string = 'body';
@Input() public scrollSpySectionId: string = '';
@Input() public tagIDList: string[] = [];
@Output() public sectionChange = new EventEmitter<string>();
timeOutScoll: any;
private currentSection: string;
constructor(private _el: ElementRef) { }
@HostListener('scroll', ['$event'])
onScroll(event: any) {
if (this.scrollOn === 'currentElement') {
}
}
@HostListener("window:scroll", ['$event'])
onWindowScroll(event: any) {
if (this.scrollOn === 'body') {
const scrollTop = window.pageYOffset; // vị trí của thanh scroll
const parentOffset = document.getElementById(this.scrollSpySectionId).offsetTop;
// check elementID match currentSection => change currentSection => emitvalue
let currentSection: string;
const elementChecks: any = this.tagIDList.map(tagId => document.getElementById(tagId));
elementChecks.forEach(element => {
if (element && element.offsetTop && element.offsetTop !== undefined && ((element.offsetTop - parentOffset) <= scrollTop)) {
currentSection = element.id;
}
});
if (currentSection !== this.currentSection) {
this.currentSection = currentSection;
this.sectionChange.emit(this.currentSection);
}
}
}
ngOnChanges(changes): void {
if(changes && changes.tagIDList && changes.tagIDList.currentValue && changes.tagIDList.currentValue.length){
this.tagIDList = this.tagIDList.flat();
}
}
}
import { Injectable } from '@angular/core';
import { MatTabChangeEvent } from '@angular/material/tabs';
@Injectable({
providedIn: 'root'
})
export class ScrollSpyService {
tabSelectedIndex = 0;
public currentSection = '';
constructor(
) {
}
setCurrentTabEvent($event: MatTabChangeEvent) {
window.scrollTo(0, 0);
this.tabSelectedIndex = $event.index;
}
setCurrentTabIndex(tabIndex) {
this.tabSelectedIndex = tabIndex;
}
onSectionChange(sectionId: string) {
this.currentSection = sectionId;
}
scrollTo(sectionId) {
this.currentSection = sectionId;
const element = document.getElementById(sectionId);
const offsetHeader = document.querySelector('#app-header')['offsetHeight']; // header-height
const bodyRect = document.body.getBoundingClientRect().top;
const elementRect = element.getBoundingClientRect().top;
let elementPosition = elementRect - bodyRect - offsetHeader;
if (document.querySelector('#fixed-tab')) {
const offsetFixedTabHeader = document.querySelector('#fixed-tab')['offsetHeight']; // header-height
elementPosition = elementPosition - offsetFixedTabHeader;
}
window.scrollTo({
top: elementPosition,
behavior: 'smooth'
});
}
getCurrentSection() {
return this.currentSection;
}
setCurrentSection(section) {
this.currentSection = section;
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ScrollSpyDirective } from './scroll-spy.directive';
import { ScrollSpyService } from './scroll-spy.service';
@NgModule({
imports: [
CommonModule,
],
declarations: [
ScrollSpyDirective
],
exports: [
ScrollSpyDirective
],
providers: [
ScrollSpyService
],
})
export class ScrollSpyModule { }