Web 组件中的拖放 API(点亮)

Drag-and-Drop API in Web Components (Lit)

提问人:Zearin 提问时间:10/4/2023 更新时间:10/5/2023 访问量:89

问:

背景信息

我设法使用普通元素和事件侦听器学习了拖放 API 的基础知识。但我想看看我是否可以使用 Web 组件重新创建相同的功能。

在“vanilla”Web Component中重新创建拖放功能时遇到了一些麻烦后,我决定尝试一下Lit(因为它建立在我喜欢的现有标准之上)。

问题

使用准系统设置进行学习。

尽管这很简单,但控制台只触发事件,但没有触发事件。dragstartdragdragend

这是课程:

class LiDragdrop extends LitElement {

    static styles = css`
        :host   { display: block }
        :host b { display: inline-block }
    `

    constructor() {
        super()
    }

    connectedCallback() {
        super.connectedCallback()

        this.setAttribute('draggable', 'true')

        this.addEventListener('dragstart',  this.onDragStart, {passive: false})
        this.addEventListener('drag',       this.onDrag)
        this.addEventListener('dragend',    this.onDragEnd)
    }

    onDragStart(evt) {
        evt.preventDefault()
        evt.dataTransfer.effectAllowed = 'move'
        evt.dataTransfer.setData('text/html', this.outerHTML)
        console.log(evt.type, `${evt.target.localName} (${evt.target.textContent})`, evt)
    }

    // fires repeatedly while dragging
    onDrag(evt) {
        evt.preventDefault()
        console.log(evt.type, `${evt.target.localName} (${evt.target.textContent})`, evt)
    }

    // always fires, even for unsuccessful drops
    onDragEnd(evt) {
        console.log(evt.type, evt.target.localName, evt)
    }

    render() {
        return html`<b>⠿</b><slot></slot>`
    }
}

customElements.define('li-dragdrop', LiDragdrop)

除了基本的 HTML 文档之外,这里是我在 :body

<li-dragdrop>Item 1</li-dragdrop>
<li-dragdrop>Item 2</li-dragdrop>

我拖动元素。我看到登录到控制台,但没有其他内容。dragstart

我不确定这是否与 Shadow DOM 事件或 Lit 有关。

我只知道我能够使用完整的 API 使用标准 HTML 元素和一些函数来实现。一旦我尝试使用 Web 组件,我就不能再进一步了。dragstart

为什么?

JavaScript HTML 事件 拖放 Web 组件

评论

0赞 Danny '365CSI' Engelman 10/6/2023
最近的一篇博客:redblobgames.com/making-of/draggable(不使用拖放 API)

答:

1赞 Danny '365CSI' Engelman 10/5/2023 #1

你的 Lit 代码所做的只是为你创建 shadowDOM。
这是下面本机 Web 组件中的 1 行。

注意:自己做拖放是一件痛苦的事情,需要几天时间才能让它按照你想要的方式工作。一旦你为一个应用程序完成了它,你就可以在每个应用程序中做到这一点。

我不做 Lit。如果此代码在从 ...那么我还有一个不使用 Lit 的理由。LitElement

最大的绊脚石:

  • 试图处理太多事件,这就是为什么下面的限制为 1 秒。
    也许使用和
    dragoverdragenterdragleave
  • dataTransfer恕我直言,这是一种痛苦。
    我通常存储有关在 parentNode 上拖动的内容的信息。想
    <chess-board>
  • 别忘了 Event 及其所有用于触摸屏的表亲touchmove
  • shadowDOM 中的 DOM 节点过多;下面的代码有一个...<style>
  • 不使用系统。每个 JS 对象都可以处理一个默认函数/方法handleEvent
  • 注意光标/抓取位置如何触发事件dragover

<div class="board">
  <drag-box color="red"></drag-box>
  <drag-box color="yellow"></drag-box>
  <drag-box color="blue"></drag-box>
</div>

<script>
  console.clear();
  customElements.define('drag-box', class extends HTMLElement {
    constructor() {
      super()
        .attachShadow({mode:'open'})
        .innerHTML = `<style>
                        :host {
                          display: inline-block;
                          width: 100px;
                          height: 100px;
                          cursor: grab;
                          background: var(--color)
                        }
                      </style>`;
      this.updatedragover = true;
    }
    connectedCallback() {
      this.color = this.color;
      this.setAttribute("draggable", true);
      // let JS default "handleEvent" on the class handle the event
      this.addEventListener('dragstart', this);
      this.addEventListener('dragend', this);
      this.addEventListener('dragover', this);
    }
    handleEvent(e) {
      if (this[e.type]) this[e.type](e); // exec method
    }
    dragover(e) {
      if (this.updatedragover) { // only once a second
        this.updatedragover = false;
        let dragcolor = this.board.dragging.color;
        console.log(dragcolor,'dragover', this.color);
        setTimeout(() => this.updatedragover = true, 1000);
      }
    }
    dragstart(e) {
      console.log(e.type, this.color);
      this.style.opacity = 0.3;
      this.board.dragging = this;
    }
    dragend(e) {
      console.log(e.type, this.color);
      this.style.opacity = 1;
    }
    get color() {
      return this.getAttribute("color") || "grey"
    }
    set color(c) {
      this.style.setProperty("--color", c);
    }
    get board(){
        return this.closest(".board");
    }
  })
</script>