Использование @ViewChild в Angular 6 для создания липкого элемента - невозможно получить позицию элемента - PullRequest
3 голосов
/ 13 мая 2019

Я использую Angular 6, пытаясь создать элемент HTML (заголовки таблицы), который расположен в середине страницы, но прилипает к верхней части страницы, когда пользователь прокручивает эту точку. Я пытаюсь захватить позицию этого элемента, используя @ ViewChild , но не могу получить его. Чего не хватает?

В настоящее время класс sticky применяется всегда, но я хочу применять класс sticky только тогда, когда позиция элемента <= to window.pageYOffset. </p>

Я пытаюсь добиться эффекта, похожего на ЭТО , используя ЭТО учебник.

Мой HTML:

<table id="tabletop" class="table" >
            <thead  >
                <tr #stickyMenu [class.sticky] = "stickyMenu" >
                    <th scope="col" id="th0">Monitoring Point</th>
                    <th scope="col" id="th1">Network Health<br><small id="more_detail">(Click icon to see more detail)</small></th>
                    <th scope="col" id="th2">Active Alerts</th>
                    <th scope="col" id="th3">Velocity (ft/s)</th>
                    <th scope="col" id="th4">Water Temperature (°F)</th>
                    <th scope="col" id="th5">Depth Readings (m)</th>               
                </tr>
            </thead>
  </table>

Css:

.sticky{
    position: fixed;
    top: 0;
    /* overflow: hidden; */
    z-index: 10;
    background-color: #fff;
    width:100%;
    padding-right:20px!important;
  }

Мой компонент:

import { Component, OnInit, ViewChild , ViewEncapsulation, HostListener, ElementRef, AfterViewInit} from '@angular/core';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { AuthService } from '../../services/auth.service';
import { SiteService } from '../../services/site.service';
import { SiteModel } from '../../models/site-model';
import { BaseComponent } from '../base.component';
import { DomSanitizer } from '@angular/platform-browser';
import { DateTime }  from 'luxon';

@Component({
  selector: 'app-site',
  templateUrl: './site.component.html',
  styleUrls: ['./site.component.css','../../../vizuly/lib/styles/vizuly.css']
})

export class SiteComponent extends BaseComponent implements OnInit, AfterViewInit {
  siteHid;
  scute_data;
  window_narrow;
  scute_data_ready: Boolean;
  sticky: boolean = false;
  elementPosition: any;  

constructor(public sanitizer: DomSanitizer, private route: ActivatedRoute, protected router: Router, private authService: AuthService, private siteService: SiteService) {
  super();
}

 @ViewChild("stickyMenu", {read: ElementRef}) tr: ElementRef;

ngOnInit() {
    this.route.paramMap.subscribe((params: ParamMap) => {
    this.siteHid = params.get('siteHid');
    this.loadSite(this.siteHid); 
 });
}

ngAfterViewInit(){
    console.log(this.tr)
    this.elementPosition = this.tr.nativeElement
    // this.elementPosition = this.stickyMenu.nativeElement.offsetTop;
    console.log(this.elementPosition)  //undefined
  }

@HostListener('window:scroll', ['$event'])
  handleScroll() {
    console.log(this.sticky, window.pageYOffset, this.elementPosition)
    this.sticky = (window.pageYOffset >= this.elementPosition) ? true : false;
    console.log(this.sticky)
   }

loadSite(siteHid){
   }

}

Ответы [ 4 ]

2 голосов
/ 13 мая 2019

Вы связываете неправильный атрибут здесь:

[class.sticky] = "stickyMenu"

должно быть

[class.sticky] = "sticky"
2 голосов
/ 14 мая 2019

Полагаю, вы хотите раскомментировать 1 строку и изменить ее на:

 ngAfterViewInit(){
    this.elementPosition = this.tr.nativeElement.offsetTop;
    console.log(this.elementPosition);
 }

Поскольку stickyMenu названо только в DOM.

1 голос
/ 14 мая 2019

У всех остальных ответов есть кусочки головоломки, но одна вещь не хватает IMO, захват this.elementPosition из this.tr.nativeElement.offsetTop возвращает слишком маленькое значение.

Просмотр MDN - HTMLElement .offsetTop

Свойство HTMLElement.offsetTop, доступное только для чтения, возвращает расстояние текущего элемента относительно вершины узла offsetParent .

но offsetParent в этом случае - <thead>, а не верхняя часть окна, что необходимо сравнить с window.scrollTop в handleScroll().
(Джованни Киоди имеет свой липкий элемент близко к верхней части окна, поэтому offsetTop работает нормально для него).

Этот вопрос SO Как получить верхнюю позицию элемента относительно окна просмотра браузера имеет лучший способ получить позицию элемента,

var viewportOffset = el.getBoundingClientRect();
// these are relative to the viewport, i.e. the window
var top = viewportOffset.top;

Так что я бы изменил ваш код на:

шаблон

<table id="tabletop" class="table" >
  <thead  >
    <tr #stickyMenu [class.sticky] = "sticky">
    ...

компонент

@ViewChild("stickyMenu", { read: ElementRef }) tr: ElementRef;

ngAfterViewInit() {
  this.elementPosition = this.tr.nativeElement.getBoundingClientRect().top;
}

@HostListener('window:scroll', ['$event']) 
handleScroll() {
  this.sticky = (window.pageYOffset >= this.elementPosition);
}
1 голос
/ 13 мая 2019

Не могли бы вы попытаться воспроизвести вашу проблему в этом блиц Я получаю смещение вершины 0 вместо неопределенного, как вы упомянули.

Также используйте позицию напрямую

console.log(this.tr)
//this.elementPosition = this.tr.nativeElement
console.log(this.tr.nativeElement);
console.log(this.tr.nativeElement.offsetTop);  
...