javascript - 等待 customElement 子级渲染以强制定位它们
问题描述
我有一个组件MyWrapper
,它由左侧的流动文本和右侧的边距组成。在页边距中,有一些MarginElement
组件应该与<mark>
文本中的标签垂直对齐。
import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('my-wrapper')
export class MyWrapper extends LitElement {
static styles = css`:host{display:flex}`
render() {
return html`
<p>
<mark id="mark1">Lorem</mark> ipsum dolor sit amet, consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse <mark id="mark2">cillium</mark>.
</p>
<div class="margin" style="width:50%">
<margin-element id="m1" related-element-id="mark1">Some note</margin-element>
<margin-element id="m2" related-element-id="mark2">Some other note</margin-element>
</div>`;
}
updated(){this.positionMargin()}
positionMargin() {
const elements = this.shadowRoot.querySelectorAll('margin-element')
elements.forEach(el => {
const relatedElement = this.shadowRoot.querySelector(`#${el.relatedElementId}`)
el.style.top = relatedElement.getBoundingClientRect().top + 'px'
})
}
}
@customElement('margin-element')
export class MarginElement extends LitElement {
static styles = css`:host {position: absolute; border: 1px solid red;}`
@property({type: String, attribute: 'related-element-id'}) relatedElementId: string
render() {return html`<slot></slot>`}
}
我想在positionMargin()
渲染MyWrapper
完成后运行该函数,但我知道调用relatedElement.getBoundingClientRect()
margin-element
本身必须被渲染。查看文档updated()
似乎是最好的电话positionMargin()
。然而,似乎到那<margin-element>
时还没有渲染 child 。因此,getBoundingClientRect()
不可用。
如何解决?
解决方案
当您返回一个它的数组时,MarginElement
它会导致它们在当前渲染更新后触发自己的渲染生命周期,但这并不可靠。
您的主要问题是您希望垂直对齐在 DOM 中相对 y 位置运行,但 x 位置堆叠在右侧。堆叠意味着<margin-element>
要么需要使用某种流/网格布局,要么它们需要相互了解,这很快就会变得令人讨厌。
相反,我会添加一个新<margin-wrapper>
元素,该元素负责绝对定位它的子元素<margin-element>
并替换<div class="margin">
.
然后在你的positionMargin
函数中设置一个你传递给它的数组<margin-wrapper .marks=${this.marksWithPositions}>
;
然后在<margin-wrapper>
's中render
,你有一个绝对可以定位的标记数组,如果你同时得到两个标记,top
你可以将后面的标记移开。
就像是:
render() {
return html`
<div class="wrapper">
<div class="content">
<p>
<mark id="mark1">Lorem</mark> ipsum dolor sit ...
<mark id="mark2">cillium</mark>.
</p>
</div>
<margin-wrapper .marks=${this.marksWithPositions}>
</div>
<button @click=${() => this.positionMargin()}>Position!</button>
`;
}
positionMargin() {
const elements = this.shadowRoot.querySelectorAll('margin-element')
const mwp = [];
elements.forEach(el => {
const markWithPos ={
top: relatedElement.getBoundingClientRect().top,
text: e.innerText
});
this.marksWithPositions = mwp; // note that this needs to be an @property or @state
}
然后在<margin-wrapper>
:
render() {
const positioned = [];
let x = 0;
for(const m of this.marks) {
if(m.top > x)
positioned.push(m);
else
positioned.push({ top: x, m.text });
x += expectedElementHeight;
}
return html`
<div class="margin">
${positioned.map(p => html`
<margin-element style=${styleMap({top: p.top + 'px'})}>${p.text}</margin-element>`)}
</div>`;
}
或者,可以使用 做一些事情ref
,例如,在每次渲染<mark ${ref(e => ...)}>
时触发一个事件。<mark>
要非常小心,<margin-wrapper>
渲染不会导致移动<mark>
元素的布局更改,因为您可以很容易地获得循环竞争条件。您可能希望到处都有明确的宽度和高度,但如果不是,您还需要调整窗口大小和滚动观察者。
推荐阅读
- c# - 如何在行项目值更改时更改 GridView 行的背景颜色?
- python - 如何将输入转换为随机类型?Python
- javascript - 在 angular rxjs 中寻找类似 OrElse 的东西在管道中查找函数
- xamarin - 在 Xamarin 表单内容视图中绑定 ICommand
- bash - 将数据写入 zip 文件
- c# - Pbkdf2 如何验证散列密码?
- java - JAVA中单词末尾出现大写字母后如何反转单词?
- visual-studio-code - 将“转到声明”分配给 Ctrl + 单击
- javascript - 关闭多个 React 组件 - 一个接一个
- pip - 即使使用 pip update 命令后,版本保持不变