在 Angular 6 中使用 @ViewChild 创建粘性元素 - 无法获取元素的位置

Using @ViewChild in Angular 6 to create a sticky element- can't get element's position

我正在使用 Angular 6 尝试创建一个 HTML 元素 (table headers),该元素位于页面中间但会坚持当用户滚动到该点时页面顶部。我正在尝试使用 @ViewChild 获取该元素的位置,但无法检索它。可能缺少什么?

目前,始终应用 'sticky' class,但我只想在元素位置 <= 到 window.pageYOffset 时应用粘性 class。

我正在尝试实现与 THIS using THIS 教程类似的效果。

我的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){
   }

}

你能否尝试重现你的问题blitzy我得到的是 offset top 0 而不是你提到的 undefined。

也直接用位置

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

您在此处绑定了错误的属性:

[class.sticky] = "stickyMenu"

应该是

[class.sticky] = "sticky"

我想你想取消注释 1 行并更改为:

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

因为 stickyMenu 仅在 DOM 中命名。

所有其他答案都有拼图,但 IMO 缺少一件事,从 this.tr.nativeElement.offsetTop 中获取 this.elementPosition 返回的值太小。

正在查看MDN - HTMLElement​.offsetTop

The HTMLElement.offsetTop read-only property returns the distance of the current element relative to the top of the offsetParent node.

但本例中的 offsetParent 是 <thead>,而不是 window 的顶部,这正是需要与 handleScroll() 中的 window.scrollTop 进行比较的地方。
(Giovanni Chiodi 的粘性元素接近 window 顶部,因此 offsetTop 适合他)。

这个SO问题How to get an element's top position relative to the browser's viewport有更好的获取元素位置的方法,

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);
}