在项目中有时候需要直接操作DOM,但是这样直接访问 DOM 会导致应用很容易受到在 XSS 攻击。所以并不建议直接访问 DOM。
在Angular 访问DOM 需要使用 Render2 来实现自定义渲染。
Renderer2类是Angular以服务的形式提供的抽象类,允许操作当前应用的元素,而不用直接访问最原始的DOM元素。
官方API
abstract class Renderer2 {
abstract data: {...}
destroyNode: ((node: any) => void) | null
abstract destroy(): void
abstract createElement(name: string, namespace?: string): any
abstract createComment(value: string): any
abstract createText(value: string): any
abstract appendChild(parent: any, newChild: any): void
abstract insertBefore(parent: any, newChild: any, refChild: any): void
abstract removeChild(parent: any, oldChild: any, isHostElement?: boolean): void
abstract selectRootElement(selectorOrNode: any, preserveContent?: boolean): any
abstract parentNode(node: any): any
abstract nextSibling(node: any): any
abstract setAttribute(el: any, name: string, value: string, namespace?: string): void
abstract removeAttribute(el: any, name: string, namespace?: string): 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: any, eventName: string, callback: (event: any) => boolean | void): () => void
}
使用一些常用的方法
创建一个Demo组件
使用命令:
ng g c operatingDOM
创建成功
wujiayudeMacBook-Pro:pages wjy$ ng g c operatingDOM
CREATE src/app/pages/operating-dom/operating-dom.component.less (0 bytes)
CREATE src/app/pages/operating-dom/operating-dom.component.html (32 bytes)
CREATE src/app/pages/operating-dom/operating-dom.component.spec.ts (671 bytes)
CREATE src/app/pages/operating-dom/operating-dom.component.ts (297 bytes)
UPDATE src/app/pages/pages.module.ts (2371 bytes)
在组件中注入 Render2
修改组件的类文件如下:
import {Component, OnInit, Renderer2} from '@angular/core';
@Component({
selector: 'app-operating-dom',
templateUrl: './operating-dom.component.html',
styleUrls: ['./operating-dom.component.less']
})
export class OperatingDOMComponent implements OnInit {
constructor(private render2: Renderer2) {
}
ngOnInit() {
}
}
创建一个元素,将这个元素添加到组件模版已有的元素中
修改组件的模版文件如下:
<nz-divider nzText="Render2 操作DOM"></nz-divider>
<div #demo></div>
<button nz-button (click)="restDemoApi1()">createElement</button>
按钮的点击事件如下:
import {Component, ElementRef, OnInit, Renderer2, ViewChild} from '@angular/core';
@Component({
selector: 'app-operating-dom',
templateUrl: './operating-dom.component.html',
styleUrls: ['./operating-dom.component.less']
})
export class OperatingDOMComponent implements OnInit {
@ViewChild('demo') demoDom: ElementRef;
constructor(private render2: Renderer2) {
}
ngOnInit() {
}
// createElement & appendChild & createText
public restDemoApi1(): void {
const divEle = this.render2.createElement('div');
const textEle = this.render2.createText('hello Render2');
this.render2.appendChild(divEle, textEle);
this.render2.appendChild(this.demoDom.nativeElement, divEle);
}
}
在这个方法中,通过 createElement 创建了一个 div 的元素,并通过 createText 创建了以一段文本节点, 将这段文本节点 通过 appendChild方法添加刚刚创建的 div 元素中,这里的div作为父元素, 文本节点作为子元素。并将添加了 文本节点 的div元素 添加到模版已经有的 demo 元素中。
从而点击的效果如下:
为DOM元素添加/删除 Attribute
在这里为之前的 div 元素增加 aria-hidden=“true” 的Attribute。
添加Attribute
修改模版文件:
<h1>Render2 操作DOM</h1>
<nz-divider nzText="创建一个DOM"></nz-divider>
<div #demo></div>
<button nz-button (click)="restDemoApi1()">createElement</button>
<nz-divider nzText="添加"></nz-divider>
<button nz-button (click)="handleSetAttribute()">添加特性</button>
修改类文件:
import {Component, ElementRef, OnInit, Renderer2, ViewChild} from '@angular/core';
import {NzNotificationService} from 'ng-zorro-antd';
@Component({
selector: 'app-operating-dom',
templateUrl: './operating-dom.component.html',
styleUrls: ['./operating-dom.component.less']
})
export class OperatingDOMComponent implements OnInit {
@ViewChild('demo') demoDom: ElementRef;
constructor(private render2: Renderer2, private notification: NzNotificationService) {
}
ngOnInit() {
}
// createElement & appendChild & createText
public restDemoApi1(): void {
const divEle = this.render2.createElement('div');
const textEle = this.render2.createText('hello Render2');
this.render2.appendChild(divEle, textEle);
this.render2.appendChild(this.demoDom.nativeElement, divEle);
this.notification.success('操作成功', '创建DOM成功~');
}
// setAttribute / removeAttribute
public handleSetAttribute(): void {
this.render2.setAttribute(this.demoDom.nativeElement, 'aria-hidden', 'true');
this.notification.success('操作成功', '添加属性成功,请打开控制台查看~');
}
}
这里重点是使用了 Render2的setAttribute方法来添加。
删除Attribute
现在在将刚刚添加的 aria-hidden 再删除。
修改模版文件:
<h1>Render2 操作DOM</h1>
<nz-divider nzText="创建一个DOM"></nz-divider>
<div #demo></div>
<button nz-button (click)="restDemoApi1()">createElement</button>
<nz-divider nzText="添加/删除Attribute"></nz-divider>
<button nz-button (click)="handleSetAttribute()">添加Attribute</button>
<button nz-button (click)="handleRemoveAttribute()">删除Attribute</button>
修改类文件:
import {Component, ElementRef, OnInit, Renderer2, ViewChild} from '@angular/core';
import {NzNotificationService} from 'ng-zorro-antd';
@Component({
selector: 'app-operating-dom',
templateUrl: './operating-dom.component.html',
styleUrls: ['./operating-dom.component.less']
})
export class OperatingDOMComponent implements OnInit {
@ViewChild('demo') demoDom: ElementRef;
constructor(private render2: Renderer2, private notification: NzNotificationService) {
}
ngOnInit() {
}
// createElement & appendChild & createText
public restDemoApi1(): void {
const divEle = this.render2.createElement('div');
const textEle = this.render2.createText('hello Render2');
this.render2.appendChild(divEle, textEle);
this.render2.appendChild(this.demoDom.nativeElement, divEle);
this.notification.success('操作成功', '创建DOM成功~');
}
// setAttribute
public handleSetAttribute(): void {
this.render2.setAttribute(this.demoDom.nativeElement, 'aria-hidden', 'true');
this.notification.success('操作成功', '添加属性成功,请打开控制台查看~');
}
// removeAttribute
public handleRemoveAttribute(): void {
this.render2.removeAttribute(this.demoDom.nativeElement, 'aria-hidden');
this.notification.success('操作成功', '删除属性成功,请打开控制台查看~');
}
}
这里重点是使用了 Render2的 removeAttribute 方法来删除。
为DOM元素添加/删除 Class
在组件的样式文件中添加一个样式
.demoTest {
background: #268ce1;
width: 200px;
height: 200px;
padding: 10px;
}
通过 Render2 将这个class添加到当前div中;
修改模版文件
<h1>Render2 操作DOM</h1>
<nz-divider nzText="创建一个DOM"></nz-divider>
<div #demo></div>
<button nz-button (click)="restDemoApi1()">createElement</button>
<nz-divider nzText="添加/删除Attribute"></nz-divider>
<button nz-button (click)="handleSetAttribute()">添加Attribute</button>
<button nz-button (click)="handleRemoveAttribute()">删除Attribute</button>
<nz-divider nzText="添加/删除 Class"></nz-divider>
<button nz-button (click)="handleSetClass()">添加Class</button>
<button nz-button (click)="handleRemoveClass()">删除Class</button>
实现 handleSetClass() 和 handleRemoveClass()
修改类文件:
import {Component, ElementRef, OnInit, Renderer2, ViewChild} from '@angular/core';
import {NzNotificationService} from 'ng-zorro-antd';
@Component({
selector: 'app-operating-dom',
templateUrl: './operating-dom.component.html',
styleUrls: ['./operating-dom.component.less']
})
export class OperatingDOMComponent implements OnInit {
@ViewChild('demo') demoDom: ElementRef;
constructor(private render2: Renderer2, private notification: NzNotificationService) {
}
ngOnInit() {
}
// createElement & appendChild & createText
public restDemoApi1(): void {
const divEle = this.render2.createElement('div');
const textEle = this.render2.createText('hello Render2');
this.render2.appendChild(divEle, textEle);
this.render2.appendChild(this.demoDom.nativeElement, divEle);
this.notification.success('操作成功', '创建DOM成功~');
}
// setAttribute
public handleSetAttribute(): void {
this.render2.removeAttribute(this.demoDom.nativeElement, 'aria-hidden');
this.notification.success('操作成功', '删除属性成功,请打开控制台查看~');
}
// removeAttribute
public handleRemoveAttribute(): void {
this.render2.removeAttribute(this.demoDom.nativeElement, 'aria-hidden');
this.notification.success('操作成功', '删除属性成功,请打开控制台查看~');
}
// addClass
public handleSetClass(): void {
this.render2.addClass(this.demoDom.nativeElement, 'demoTest');
this.render2.addClass(this.demoDom.nativeElement, 'demoTest1');
this.notification.success('操作成功', '添加class成功~');
}
// removeClass
public handleRemoveClass(): void {
this.render2.removeClass(this.demoDom.nativeElement, 'demoTest');
this.notification.success('操作成功', '删除class成功~');
}
}
这里重点使用了 addClass 添加Class 和 removeClass 删除Class
需要注意的是,这里在添加两个甚至更多个class属性的时候,要通过addClass方法一个一个添加
假设有两个class:“demoTest”、“demoTest1”,添加到同一个元素上,以下添加方式会报错的:
this.render2.addClass(this.demoDom.nativeElement, 'demoTest demoTest1');
要通过以下方式添加:
this.render2.addClass(this.demoDom.nativeElement, 'demoTest');
this.render2.addClass(this.demoDom.nativeElement, 'demoTest1');
这样添加了的class就是:“demoTest demoTest1”
为DOM元素添加/删除 style
在添加了 demoTest 这个样式的基础上,在添加一个样式 border-radius 值为100px;
修改模版文件:
<h1>Render2 操作DOM</h1>
<nz-divider nzText="创建一个DOM"></nz-divider>
<div #demo></div>
<button nz-button (click)="restDemoApi1()">createElement</button>
<nz-divider nzText="添加/删除Attribute"></nz-divider>
<button nz-button (click)="handleSetAttribute()">添加Attribute</button>
<button nz-button (click)="handleRemoveAttribute()">删除Attribute</button>
<nz-divider nzText="添加/删除 Class"></nz-divider>
<button nz-button (click)="handleSetClass()">添加Class</button>
<button nz-button (click)="handleRemoveClass()">删除Class</button>
<nz-divider nzText="添加/删除 style"></nz-divider>
<button nz-button (click)="handleSetStyle()">添加style</button>
<button nz-button (click)="handleRemoveStyle()">删除style</button>
实现 handleSetStyle() 和 handleRemoveStyle() 方法
import {Component, ElementRef, OnInit, Renderer2, ViewChild} from '@angular/core';
import {NzNotificationService} from 'ng-zorro-antd';
@Component({
selector: 'app-operating-dom',
templateUrl: './operating-dom.component.html',
styleUrls: ['./operating-dom.component.less']
})
export class OperatingDOMComponent implements OnInit {
@ViewChild('demo') demoDom: ElementRef;
constructor(private render2: Renderer2, private notification: NzNotificationService) {
}
ngOnInit() {
}
// createElement & appendChild & createText
public restDemoApi1(): void {
const divEle = this.render2.createElement('div');
const textEle = this.render2.createText('hello Render2');
this.render2.appendChild(divEle, textEle);
this.render2.appendChild(this.demoDom.nativeElement, divEle);
this.notification.success('操作成功', '创建DOM成功~');
}
// setAttribute
public handleSetAttribute(): void {
this.render2.removeAttribute(this.demoDom.nativeElement, 'aria-hidden');
this.notification.success('操作成功', '删除属性成功,请打开控制台查看~');
}
// removeAttribute
public handleRemoveAttribute(): void {
this.render2.removeAttribute(this.demoDom.nativeElement, 'aria-hidden');
this.notification.success('操作成功', '删除属性成功,请打开控制台查看~');
}
// addClass
public handleSetClass(): void {
this.render2.addClass(this.demoDom.nativeElement, 'demoTest');
this.notification.success('操作成功', '添加class成功~');
}
// removeClass
public handleRemoveClass(): void {
this.render2.removeClass(this.demoDom.nativeElement, 'demoTest');
this.notification.success('操作成功', '删除class成功~');
}
// setStyle
public handleSetStyle(): void {
this.render2.setStyle(this.demoDom.nativeElement, 'border-radius', '100px');
this.notification.success('操作成功', '删除 Style 成功~');
}
// removeStyle
public handleRemoveStyle(): void {
this.render2.removeStyle(this.demoDom.nativeElement, 'border-radius');
this.notification.success('操作成功', '删除 Style 成功~');
}
}
这里重点使用了 setStyle 添加 Style 和 removeStyle 删除 Style,但是也需要注意的是,这里通过setStyle添加的样式是内联样式
为DOM元素添加 Property
为一个input元素添加value属性,值为demo
修改模版文件:
<h1>Render2 操作DOM</h1>
<nz-divider nzText="创建一个DOM"></nz-divider>
<div #demo></div>
<button nz-button (click)="restDemoApi1()">createElement</button>
<nz-divider nzText="添加/删除Attribute"></nz-divider>
<button nz-button (click)="handleSetAttribute()">添加Attribute</button>
<button nz-button (click)="handleRemoveAttribute()">删除Attribute</button>
<nz-divider nzText="添加/删除 Class"></nz-divider>
<button nz-button (click)="handleSetClass()">添加Class</button>
<button nz-button (click)="handleRemoveClass()">删除Class</button>
<nz-divider nzText="添加/删除 style"></nz-divider>
<button nz-button (click)="handleSetStyle()">添加style</button>
<button nz-button (click)="handleRemoveStyle()">删除style</button>
<nz-divider nzText="添加 Property"></nz-divider>
<nz-form-item>
<nz-form-control nzSpan="4" nz-col>
<label>
<input nz-input #demoInput>
</label>
</nz-form-control>
<nz-form-control nzSpan="4" nz-col>
<button nz-button (click)="handleSetProperty()">添加 Property</button>
</nz-form-control>
</nz-form-item>
实现 handleSetProperty()方法
修改类文件:
import {Component, ElementRef, OnInit, Renderer2, ViewChild} from '@angular/core';
import {NzNotificationService} from 'ng-zorro-antd';
@Component({
selector: 'app-operating-dom',
templateUrl: './operating-dom.component.html',
styleUrls: ['./operating-dom.component.less']
})
export class OperatingDOMComponent implements OnInit {
@ViewChild('demo') demoDom: ElementRef;
@ViewChild('demoInput') demoInput: ElementRef;
constructor(private render2: Renderer2, private notification: NzNotificationService) {
}
ngOnInit() {
}
// createElement & appendChild & createText
public restDemoApi1(): void {
const divEle = this.render2.createElement('div');
const textEle = this.render2.createText('hello Render2');
this.render2.appendChild(divEle, textEle);
this.render2.appendChild(this.demoDom.nativeElement, divEle);
this.notification.success('操作成功', '创建DOM成功~');
}
// setAttribute
public handleSetAttribute(): void {
this.render2.removeAttribute(this.demoDom.nativeElement, 'aria-hidden');
this.notification.success('操作成功', '删除属性成功,请打开控制台查看~');
}
// removeAttribute
public handleRemoveAttribute(): void {
this.render2.removeAttribute(this.demoDom.nativeElement, 'aria-hidden');
this.notification.success('操作成功', '删除属性成功,请打开控制台查看~');
}
// addClass
public handleSetClass(): void {
this.render2.addClass(this.demoDom.nativeElement, 'demoTest');
this.notification.success('操作成功', '添加class成功~');
}
// removeClass
public handleRemoveClass(): void {
this.render2.removeClass(this.demoDom.nativeElement, 'demoTest');
this.notification.success('操作成功', '删除class成功~');
}
// setStyle
public handleSetStyle(): void {
this.render2.setStyle(this.demoDom.nativeElement, 'border-radius', '100px');
this.notification.success('操作成功', '删除 Style 成功~');
}
// removeStyle
public handleRemoveStyle(): void {
this.render2.removeStyle(this.demoDom.nativeElement, 'border-radius');
this.notification.success('操作成功', '删除 Style 成功~');
}
// setProperty
public handleSetProperty(): void {
this.render2.setProperty(this.demoInput.nativeElement, 'value', 'demo');
this.notification.success('操作成功', '添加 Property 成功~');
}
}
这里主要使用了 setProperty 方法
为DOM元素设置监听事件
为之前的input设置 一个键盘监听事件
修改类文件:
constructor(private render2: Renderer2, private notification: NzNotificationService) {
}
ngOnInit() {
this.render2.listen(this.demoInput.nativeElement, 'keydown', event => {
console.log(event);
});
}
运行结果:
代码提交到GitHub