某天下午,同事向我请教了一个问题,需求是有两个table,当table存在滚动条时,在某一个table上滚动,那么另一个table也要同步滚动,巧的是我也不会,但是我闲着没事,帮着研究了一下。
技术栈是: angular8 + ng-zorro组件库
先看效果和代码:

html代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <div class="syn-margin-left-24" style="display: flex;"> <div> <nz-table [nzData]="leftData" [nzScroll]="{y: '300px'}" style="width: 95%;" [nzShowPagination]=false> <thead> <tr> <th >Name</th> <th >Age</th> <th >Address</th> </tr> </thead> <tbody> <tr *ngFor="let data of leftData"> <td>{{data.name}}</td> <td>{{data.age}}</td> <td>{{data.address}}</td> </tr> </tbody> </nz-table> </div> <div> <nz-table [nzData]="rightData" [nzScroll]="{y: '300px'}" style="width: 95%;" [nzShowPagination]=false> <thead> <tr> <th>Name</th> <th>Age</th> <th>Address</th> </tr> </thead> <tbody> <tr *ngFor="let data of rightData"> <td>{{data.name}}</td> <td>{{data.age}}</td> <td>{{data.address}}</td> </tr> </tbody> </nz-table> </div> </div>
|
ts代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| import { StorageUtil } from './../shared/utils/storage-util'; import { AfterViewInit, Component } from '@angular/core'; class Person { name?: string; age?: number; address?: string; } @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html', styleUrls: ['./dashboard.component.scss'] }) export class DashboardComponent implements AfterViewInit { leftData: Person[] = this.generateItems('Edward King',32,'London, Park Lane no.',25); rightData: Person[] = this.generateItems('John Brown',42,'New York No. 1 Lake Park',15); ngAfterViewInit(): void { const element = document.querySelectorAll('.ant-table-body'); const leftTable = element[0]; const rightTable = element[1]; const scale = (leftTable.scrollHeight - leftTable.clientHeight) / (rightTable.scrollHeight - rightTable.clientHeight); if (leftTable?.scrollHeight && leftTable.clientHeight && leftTable?.scrollHeight && leftTable.clientHeight) { let flag = true; leftTable.addEventListener('mouseover',() => { flag = false; leftTable.addEventListener('scroll',() => { if (!flag) { rightTable.scrollTop = leftTable.scrollTop / scale; rightTable.scrollLeft = leftTable.scrollLeft; } }) }); rightTable.addEventListener('mouseover',() => { flag = true; rightTable.addEventListener('scroll',() => { if (flag) { leftTable.scrollTop = rightTable.scrollTop * scale; leftTable.scrollLeft = rightTable.scrollLeft; } }) }); } } generateItems(name: string,age: number,address: string,size: number): Person[] { const res: Person[] = []; for (let i = 0; i < size; i++) { res.push( { name: `${name}${i}`, age: age + i, address: `${address}${i}` } ) } return res; } }
|
看ts代码的第23行,querySelectAll()方法可以获取所有class名为"ant-table-body"的元素,这个class是ng-zorro框架内部的类,可以通过浏览器的审查元素看到:

可能你会有疑问为什么我会找这个class呢?这是因为我们要监听存在滚动条的容器的scroll事件,所以自然的要找到这个容器。
现在已经把Dom找出来了,下一步就是设置让两个table的scrollTop和scrollLeft的值相同即可。
有两个需要注意的地方:
-
上面的代码我们设置了个flag值,是为了防止左右两个table相互赋值导致滑动缓慢,同时也可减少浏览器的性能消耗,你可以去掉flag字段看看,滑动会变卡。
-
scale值是为了防止两个table的高度不一样设置的,这样的话滑动的时候两个table会同时到达底部或顶部。如果两个table的高度相同那scale就没啥意义了,加不加都一样。
完整的思路如下:
-
找到关键点,使两个table同步滚动的关键就是找到并设置两个table滚动区域的scrollTop、scrollLeft的值相同即可。
-
因为用的是ng-zorro框架,所以通过审查元素定位到某个div。
-
通过querySelectorAll()方法获取两个table的dom对象,获取dom对象的方式有很多种,在这里只能用querySelectorAll()方法。
-
添加监听事件,先监听mouseover再监听scroll事件。
-
设置两个容器的scrollTop和scrollLeft的值相同。
-
考虑反思有没有问题,比如两个table高度不一样等,然后再想怎么完善。
解决该问题用到了很多基础的js的知识,比如原生的鼠标监听事件,mouseover、scroll等,还有scrollTop、scrollHeigh、clientHeight等属性的理解,由此看出有一个好的基础还是挺重要的!
以上。笔者水平有限,若有错误敬请指正,不明白的地方也可评论区留言交流~