Angular 8单页应用点击子组件锚点滚动到父元素失败求助
Hey there! I totally get where you're stuck—trying to scroll to elements in a parent component from a child menu component can be tricky at first, especially since Angular keeps components' DOMs isolated from each other. Let's break down why your current code isn't working, then fix it with proper Angular practices.
Why Your Current Code Fails
Your header.component.ts uses document.querySelector(''+element+'') to find the target section, but here's the catch: each Angular component has its own DOM context. The #aboutus and #contact elements live in the HomeComponent's template, not the HeaderComponent's. So when the header tries to query the document, it can't find those elements, hence the Cannot read property 'scrollIntoView' of null error.
Solution 1: Use Output Events + ViewChild (Recommended)
This follows Angular's official component communication patterns—we'll have the header emit a "scroll request" event, and the home component handle the scrolling using references to its own elements.
Step 1: Update Header Component to Emit Events
Modify header.component.ts to send a signal when a menu item is clicked:
import { Component, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-header', templateUrl: './header.component.html' }) export class HeaderComponent { // Create an event emitter to send scroll requests to the parent @Output() scrollRequest = new EventEmitter<string>(); navigateTo(element: string) { // Emit the target element name instead of trying to scroll directly this.scrollRequest.emit(element); } }
Step 2: Update Home Component to Handle Scrolling
In home.component.html, listen for the scrollRequest event and add template references to your sections:
<!-- Listen for the scroll event from the header --> <app-header (scrollRequest)="handleScroll($event)"></app-header> ... <section #aboutus>About us details</section> ... <section #contact>Contact form section</section>
Then in home.component.ts, use ViewChild to grab the elements and handle the scroll:
import { Component, ViewChild, ElementRef } from '@angular/core'; @Component({ selector: 'app-home', templateUrl: './home.component.html' }) export class HomeComponent { // Get references to the sections using their template variables @ViewChild('aboutus') aboutUsElement: ElementRef; @ViewChild('contact') contactElement: ElementRef; handleScroll(target: string) { let targetElement: ElementRef; // Match the emitted target to the correct element switch(target) { case 'aboutus': targetElement = this.aboutUsElement; break; case 'contact': targetElement = this.contactElement; break; default: return; // Exit if target is unknown } // Trigger the scroll (add smooth behavior for nicer UX) targetElement.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }
Solution 2: Pass Element References Directly to Header
If your component structure is simple, you can pass the home component's element references directly to the header via @Input:
Step 1: Update Home Component to Pass References
<!-- Pass the section references to the header as inputs --> <app-header [aboutElement]="aboutus" [contactElement]="contact"></app-header> ... <section #aboutus>About us details</section> ... <section #contact>Contact form section</section>
Step 2: Update Header Component to Use the References
import { Component, Input, ElementRef } from '@angular/core'; @Component({ selector: 'app-header', templateUrl: './header.component.html' }) export class HeaderComponent { // Receive the element references from the parent @Input() aboutElement: ElementRef; @Input() contactElement: ElementRef; navigateTo(element: string) { let targetElement: ElementRef; // Select the correct element based on the click if (element === 'aboutus') { targetElement = this.aboutElement; } else if (element === 'contact') { targetElement = this.contactElement; } // Scroll if we have a valid element if (targetElement) { targetElement.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } }
Key Takeaway
Avoid using document.querySelector to access elements across components—Angular's component isolation means those elements aren't in the child component's DOM context. Using @Output/@Input with ViewChild keeps your code aligned with Angular's best practices and makes it more maintainable.
内容的提问来源于stack exchange,提问作者ShivarajRH

