ngx-color-picker滚动页面时定位偏移问题求助
解决ngx-color-picker滚动后定位偏移问题
我用Angular的@for指令实现了一个表格,每行都有修改行背景色的按钮,ngx-color-picker功能正常,但当页面因列表过长滚动时,颜色选择器不再锚定点击的按钮,而是向上偏移几行显示。需要让选择器始终精准定位到点击按钮,不受页面滚动影响。
相关代码
HTML
<tbody> @for (element of elements(); track element.ID; let i = $index) { <tr class="table-element-row" cdkDrag [style.background-color]="element.BackgroundColor"> <td [style]="{ width: getColumnWidth(0) }"> <i class="fas fa-palette ms-1" style="font-size: 20px; cursor: pointer" [(colorPicker)]="element.BackgroundColor" [cpPresetColors]="rowColors" [cpPresetLabel]="'Presets'" (colorPickerClose)="changeColorSelected($event, element)" [cpToggle]="false" [cpPosition]="'auto'" [cpPositionRelativeToArrow]="true" (click)="fixPickerPosition($event, element)" #colorPicker></i> </td> <td [style]="{ width: getColumnWidth(1) }"> <ng-container [ngTemplateOutlet]="elementStatusTemplate" [ngTemplateOutletContext]="{ row: element }"></ng-container> </td> <td [style]="{ width: getColumnWidth(2) }"> {{ element.PMCDenomination?? element.CDenomination }} </td> <td [style]="{ width: getColumnWidth(3) }">{{ element.SbName ?? element.Name }}</td> <td [style]="{ width: getColumnWidth(4) }">{{ element.SbName }}</td> <td>{{ getPMULabel(element) }}</td> <td [style]="{ width: getColumnWidth(5) }"> <ng-container [ngTemplateOutlet]="taskCalendarEventTemplate" [ngTemplateOutletContext]="{ row: element }"></ng-container> </td> <td [style]="{ width: getColumnWidth(6) }"> <ng-container [ngTemplateOutlet]="elementNTemplate" [ngTemplateOutletContext]="{ row: element }"></ng-container> </td> </tr> } </tbody>
TypeScript
fixPickerPosition(event: Event, el: any) { const target = event.target as HTMLElement; const rect = el.getBoundingClientRect(); const scrollTop = window.scrollY || document.documentElement.scrollTop; const scrollLeft = window.scrollX || document.documentElement.scrollLeft; const pickerElement = this.colorPicker.nativeElement; this.renderer.setStyle(pickerElement, "top", `${rect.top + scrollTop}px`); this.renderer.setStyle(pickerElement, "left", `${rect.left + scrollLeft}px`); }
问题分析
原代码存在两个核心问题:
#colorPicker绑定在触发按钮(<i>标签)上,所以this.colorPicker.nativeElement操作的是按钮本身,而非ngx-color-picker动态生成的弹窗,定位代码完全无效。- 使用数据对象
el调用getBoundingClientRect(),而非点击的DOM元素,获取的位置根本不是按钮的实际位置。
解决方案
方法一:利用插件内置的cpAppendTo属性(推荐)
通过将颜色选择器弹窗附加到按钮的父容器,让弹窗随父元素滚动,自动保持正确定位:
修改HTML,给按钮所在的<td>添加模板变量,并设置[cpAppendTo]:
<td [style]="{ width: getColumnWidth(0) }" #pickerContainer> <i class="fas fa-palette ms-1" style="font-size: 20px; cursor: pointer" [(colorPicker)]="element.BackgroundColor" [cpPresetColors]="rowColors" [cpPresetLabel]="'Presets'" (colorPickerClose)="changeColorSelected($event, element)" [cpToggle]="false" [cpPosition]="'bottom'" <!-- 固定位置,避免auto模式的自动偏移 --> [cpAppendTo]="pickerContainer"></i> </td>
删除原有的(click)="fixPickerPosition(...)"和对应的TS函数即可,无需额外代码。
方法二:手动定位弹窗(自定义场景)
如果需要自定义弹窗位置,可在弹窗打开时获取其DOM元素,根据触发按钮的位置手动设置坐标:
修改HTML
<i class="fas fa-palette ms-1" style="font-size: 20px; cursor: pointer" [(colorPicker)]="element.BackgroundColor" [cpPresetColors]="rowColors" [cpPresetLabel]="'Presets'" (colorPickerClose)="changeColorSelected($event, element)" [cpToggle]="false" [cpPosition]="'bottom'" [cpPositionRelativeToArrow]="false" (colorPickerOpen)="fixPickerPosition($event.target)"></i>
修改TS函数
fixPickerPosition(triggerElement: HTMLElement) { // 获取ngx-color-picker生成的弹窗元素 const pickerPopup = document.querySelector('.color-picker-popup'); if (!pickerPopup) return; const triggerRect = triggerElement.getBoundingClientRect(); const scrollTop = window.scrollY || document.documentElement.scrollTop; const scrollLeft = window.scrollX || document.documentElement.scrollLeft; // 设置弹窗相对于触发按钮的位置(示例为按钮下方) this.renderer.setStyle(pickerPopup, 'position', 'absolute'); this.renderer.setStyle(pickerPopup, 'top', `${triggerRect.bottom + scrollTop}px`); this.renderer.setStyle(pickerPopup, 'left', `${triggerRect.left + scrollLeft}px`); this.renderer.setStyle(pickerPopup, 'transform', 'none'); // 禁用插件的自动变换 } // 在弹窗关闭时清除自定义样式,避免影响其他选择器 changeColorSelected(event: any, element: any) { const pickerPopup = document.querySelector('.color-picker-popup'); if (pickerPopup) { this.renderer.removeStyle(pickerPopup, 'position'); this.renderer.removeStyle(pickerPopup, 'top'); this.renderer.removeStyle(pickerPopup, 'left'); this.renderer.removeStyle(pickerPopup, 'transform'); } // 原有业务逻辑... }
内容的提问来源于stack exchange,提问作者Jany
相关产品推荐
相关产品推荐

