deepthan/blog-angular

Angular如何操作DOM?

deepthan opened this issue · 0 comments

Angular不建议直接操作DOM元素,正如 ElementRef 函数的源码中介绍的一样:

export declare class ElementRef {
    /**
     * The underlying native element or `null` if direct access to native elements is not supported
     * (e.g. when the application runs in a web worker).
     *
     * <div class="callout is-critical">
     *   <header>Use with caution</header>
     *   <p>
     *    Use this API as the last resort when direct access to DOM is needed. Use templating and
     *    data-binding provided by Angular instead. Alternatively you take a look at {@link Renderer}
     *    which provides API that can safely be used even when direct access to native elements is not
     *    supported.
     *   </p>
     *   <p>
     *    Relying on direct DOM access creates tight coupling between your application and rendering
     *    layers which will make it impossible to separate the two and deploy your application into a
     *    web worker.
     *   </p>
     * </div>
     * @stable
     */
    nativeElement: any;
    constructor(nativeElement: any);
}
export declare class ElementRef {
    /**
     * 如果不支持原生元素直接访问底层的本地元素或`null`
     * (e.g. 当应用程序在一个网络工作者运行).
     *
     * <div class="callout is-critical">
     *   <header>请谨慎使用</header>
     *   <p>
     *  当需要直接访问DOM时,使用此API作为最后的手段。 使用模板和
 数据绑定由Angular提供。 或者,您可以查看{@link Renderer}
 它提供的API即使在直接访问本地元素时也可以安全地使用
支持的。
     *   </p>
     *   <p>
     *   依靠直接的DOM访问可以在应用程序和呈现之间建立紧密的耦合
     *  层将使它不可能分开两个和部署你的应用程序到一个
     *  网络工作者。
     *   </p>
     * </div>
     * @stable
     */
    nativeElement: any;
    constructor(nativeElement: any);
}

ElementRef 获取DOM (ref:参考)

在应用层直接操作 DOM,就会造成应用层与渲染层之间强耦合,导致我们的应用无法运行在不同环境,所以不能直接操作 DOM 元素。
在浏览器中,通过 ElementRef 我们就可以封装不同平台下视图层中的 DOM 元素,最后借助于 Angular 提供的强大的依赖注入特性,我们就可以轻松地访问到 DOM 元素。

// HTML
<div class='deep'>蛮子</div>
// TS 
import { Component, ElementRef, ngAfterViewInit } from '@angular/core';
export class AppComponent {
  constructor(private elementRef: ElementRef) { }
  ngAfterViewInit(){
    let divEle = this.elementRef.nativeElement.querySelector('deep');
    console.log(divEle); ->打印出 '<div class='deep'>蛮子</div>'
  }
}

内置属性装饰器 @ViewChild 改变其样式

// HTML
<div #deep>蛮子</div>
// TS 
import { Component, ElementRef, ViewChild,  ngAfterViewInit } from '@angular/core';
export class AppComponent {
  @ViewChild('deep')  willChangeDiv: ElementRef;
  constructor(private elementRef: ElementRef) {}
  ngAfterViewInit(){
    this.willChangeDiv.nativeElement.style.backgroundColor = 'red';
  }
}

renderer2 对象提供的 API 优雅改变样式

// HTML
<div #deep>蛮子</div>
// TS 
import { Component, ElementRef, ViewChild,  ngAfterViewInit, Renderer  } from '@angular/core';
export class AppComponent {
  @ViewChild('deep')  willChangeDiv: ElementRef;
  constructor(private elementRef: ElementRef) {}
  ngAfterViewInit(){
    this.renderer.setStyle(this.willChangeDiv.nativeElement, 'backgroundColor', 'red');
  }
}

renderer2 提供了哪些方法?


 export declare abstract class render2 { 
    /**
     * This field can be used to store arbitrary data on this renderer instance.
     * This is useful for renderers that delegate to other renderers.
     */
    readonly abstract data: {
        [key: string]: any;
    };
    abstract destroy(): void;
    abstract createElement(name: string, namespace?: string | null): any;
    abstract createComment(value: string): any;
    abstract createText(value: string): any;
    /**
     * This property is allowed to be null / undefined,
     * in which case the view engine won't call it.
     * This is used as a performance optimization for production mode.
     */
    destroyNode: ((node: any) => void) | null;
    abstract appendChild(parent: any, newChild: any): void;
    abstract insertBefore(parent: any, newChild: any, refChild: any): void;
    abstract removeChild(parent: any, oldChild: any): void;
    abstract selectRootElement(selectorOrNode: string | any): any;
    /**
     * Attention: On WebWorkers, this will always return a value,
     * as we are asking for a result synchronously. I.e.
     * the caller can't rely on checking whether this is null or not.
     */
    abstract parentNode(node: any): any;
    /**
     * Attention: On WebWorkers, this will always return a value,
     * as we are asking for a result synchronously. I.e.
     * the caller can't rely on checking whether this is null or not.
     */
    abstract nextSibling(node: any): any;
    abstract setAttribute(el: any, name: string, value: string, namespace?: string | null): void;
    abstract removeAttribute(el: any, name: string, namespace?: string | null): void;
    abstract addClass(el: any, name: string): void;
    abstract removeClass(el: any, name: string): void;
    abstract setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2): void;
    abstract removeStyle(el: any, style: string, flags?: RendererStyleFlags2): void;
    abstract setProperty(el: any, name: string, value: any): void;
    abstract setValue(node: any, value: string): void;
    abstract listen(target: 'window' | 'document' | 'body' | any, eventName: string, callback: (event: any) => boolean | void): () => void;
}