微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

等待 customElement 子项呈现以命令式地定位它们

如何解决等待 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> 。因此,getBoundingClientRect() 不可用。

如何解决这个问题?

解决方法

多亏了 Lit 团队的帮助(谢谢!),我才弄明白了。解决方案是在解决 updateComplete 承诺之前等待子进程呈现。

这可以通过查询所有 <margin-element> 孩子并等待他们各自的 updateComplete 承诺解决来实现(另见 here)。

因此,一旦我添加了上面的代码

async getUpdateComplete() {
        await super.getUpdateComplete();
        const marginElements = Array.from(this.shadowRoot.querySelectorAll('margin-element'));
        await Promise.all(marginElements.map(el => el.updateComplete));
        return true;
    }

这是一个带有 complete example 的灯光游乐场。

,

当您返回一个 MarginElement 数组时,它会起作用,因为它会导致它们在当前渲染更新后触发自己的渲染生命周期,但它并不可靠。

您的主要问题是您希望垂直对齐偏离 DOM 中的相对 y 位置,但 x 位置堆叠在右侧。堆叠意味着 <margin-element> 要么需要使用某种流/网格布局,要么需要相互了解,这会很快变得令人讨厌。

相反,我会添加一个新的 <margin-wrapper> 元素,该元素负责绝对定位它的子元素 <margin-element> 并替换 <div class="margin">

然后在您的 positionMargin 函数中设置一个您传递给它的数组 <margin-wrapper .marks=${this.marksWithPositions}>;

然后在 <margin-wrapper>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> 元素的布局更改,因为您很容易获得循环竞争条件。您可能希望到处都有明确的宽度和高度,但如果没有,您还需要调整窗口大小和滚动观察者。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?