Angular学习笔记64:使用Render2安全操作DOM元素

在项目中有时候需要直接操作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方法来添加。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J4kQ9Wl7-1570340304658)(evernotecid://621D2FF6-4E72-4E02-9043-55F31F42819B/appyinxiangcom/22553815/ENResource/p183)]

删除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 方法来删除。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TkhXJJBd-1570340304659)(evernotecid://621D2FF6-4E72-4E02-9043-55F31F42819B/appyinxiangcom/22553815/ENResource/p184)]

为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');

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8c7T6ONi-1570340304660)(evernotecid://621D2FF6-4E72-4E02-9043-55F31F42819B/appyinxiangcom/22553815/ENResource/p186)]

要通过以下方式添加:

this.render2.addClass(this.demoDom.nativeElement, 'demoTest');
    this.render2.addClass(this.demoDom.nativeElement, 'demoTest1');

这样添加了的class就是:“demoTest demoTest1”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nnA8sfui-1570340304661)(evernotecid://621D2FF6-4E72-4E02-9043-55F31F42819B/appyinxiangcom/22553815/ENResource/p188)]

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

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MIe3mshL-1570340304666)(evernotecid://621D2FF6-4E72-4E02-9043-55F31F42819B/appyinxiangcom/22553815/ENResource/p192)]

代码提交到GitHub

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值