Обмен "Custom Element" без вызова connectedCallback

Я создаю приложение для выборов, которое потребует переключения элементов в списке. У меня есть следующие пользовательские веб-компоненты. Я обрезал нерелевантные функции из класса для краткости.

// Position
// ---------------------------------------
class Position extends HTMLElement {
    constructor(title) {
        super();
        this.title = title
    }

    connectedCallback() {
        this.className = "portlet";
        // Copy the HTML template
        var template = document.querySelector("#template-e-position");
        this.appendChild(document.importNode(template.content, true));
        // Create the title tag
        var title = this.querySelector(".title");
        title.innerHTML = this.title;
        // Create event listener for swap links
        this.querySelector(".moveUp").addEventListener("click", function(e) {
            swapWithPrevSibling(that);
        });
        this.querySelector(".moveDown").addEventListener("click", function(e) {
            swapWithNextSibling(that);
        });
    }
}
customElements.define('e-position', Position);

// Candidate
// ---------------------------------------
class Candidate extends HTMLElement {
  constructor(name) {
    super();
    this.name = name;
  }
  connectedCallback() {
        // Copy the HTML template
        var template = document.querySelector("#template-e-candidate");
        this.appendChild(document.importNode(template.content, true));
        // Create the title tag
        var name = this.querySelector(".name");
        name.innerHTML = this.name;
        // Create event listener for delete link
        var a = this.querySelector("a.delete");
        var that = this;
        a.addEventListener('click', function(e) { return that.delete(e) }, false);
    }

    delete(event) {
        deleteNode(this);
    }

}
customElements.define('e-candidate', Candidate);

У меня есть функции подкачки:

function swapWithPrevSibling (elm) {
    elm.parentNode.insertBefore(elm,elm.previousSibling)
}

function swapWithNextSibling (elm) {
    elm.parentNode.insertBefore(elm.nextSibling,elm)
}

Я использую следующий шаблон для создания пользовательских элементов:

<template id="template-e-position">
    <div class="header">
        <span class="title"></span>
        <div class="edit-menu">
            <a class="moveUp">&uarr;</a>
            <a class="moveDown">&darr;</a>
            <a class="delete">X</a>
        </div>
    </div>
    <div class="candidate-list">
    </div>
    <form class="add-candidate">
        <input type="text" />
        <input type="submit" value="Add candidate">
    </form>
</template>

<template id="template-e-candidate">
    <span class="name"></span>
    <div class="edit-menu">
        <a class="moveUp">&uarr;</a>
        <a class="moveDown">&darr;</a>
        <a class="delete">X</a>
    </div>
</template>

Поскольку я создаю пользовательские элементы из шаблонов HTML, мне нужно клонировать шаблоны в connectedCallback() (поскольку добавление потомков в конструктор запрещено в v1). Результатом этого является то, что когда я вызываю функцию подкачки для "позиций" в списке, это приводит к повторному клонированию шаблона и добавлению ненужных элементов DOM к элементам Position и Candidate.

Например, результат после замены должен быть:

<e-position title="Vice-President" class="portlet">
    <div class="header">
        <span class="title">Vice-President</span>
        <div class="edit-menu">
            <a class="moveUp">↑</a>
            <a class="moveDown">↓</a>
            <a class="delete">X</a>
        </div>
    </div>
    <div class="candidate-list">
<e-candidate>
    <span class="name">Evan</span>
    <div class="edit-menu">
        <a class="moveUp">↑</a>
        <a class="moveDown">↓</a>
        <a class="delete">X</a>
    </div>
</e-candidate>
<e-candidate>
    <span class="name">Steph</span>
    <div class="edit-menu">
        <a class="moveUp">↑</a>
        <a class="moveDown">↓</a>
        <a class="delete">X</a>
    </div>
</e-candidate>
    </div>
    <form class="add-candidate">
        <input type="text">
        <input value="Add candidate" type="submit">
    </form>
</e-position>

Но это заканчивается путаницей:

<e-position title="Vice-President" class="portlet">
    <div class="header">
        <span class="title">Vice-President</span>
        <div class="edit-menu">
            <a class="moveUp">↑</a>
            <a class="moveDown">↓</a>
            <a class="delete">X</a>
        </div>
    </div>
    <div class="candidate-list">
    <e-candidate>
    <span class="name">Evan</span>
    <div class="edit-menu">
        <a class="moveUp">↑</a>
        <a class="moveDown">↓</a>
        <a class="delete">X</a>
    </div>

    <span class="name"></span>
    <div class="edit-menu">
        <a class="moveUp">↑</a>
        <a class="moveDown">↓</a>
        <a class="delete">X</a>
    </div>
    </e-candidate><e-candidate>
    <span class="name">Steph</span>
    <div class="edit-menu">
        <a class="moveUp">↑</a>
        <a class="moveDown">↓</a>
        <a class="delete">X</a>
    </div>

    <span class="name"></span>
    <div class="edit-menu">
        <a class="moveUp">↑</a>
        <a class="moveDown">↓</a>
        <a class="delete">X</a>
    </div>
    </e-candidate>
    </div>
    <form class="add-candidate">
        <input type="text">
        <input value="Add candidate" type="submit">
    </form>

    <div class="header">
        <span class="title"></span>
        <div class="edit-menu">
            <a class="moveUp">↑</a>
            <a class="moveDown">↓</a>
            <a class="delete">X</a>
        </div>
    </div>
    <div class="candidate-list">
    </div>
    <form class="add-candidate">
        <input type="text">
        <input value="Add candidate" type="submit">
    </form>
</e-position>

Есть ли лучший способ клонировать шаблоны HTML, чтобы мне не нужно было добавлять элементы в connectedCallback? Если нет, как я могу эффективно поменять местами, не привлекая все дополнительные элементы? Обратите внимание, что я не хочу использовать jQuery, поскольку я хочу легкое приложение.

Я видел это, и это не работает, потому что в итоге вызывается метод connectedCallback и вставляется Как поменять местами дочерние узлы DOM в JavaScript?

1 ответ

Решение

Есть несколько решений:

  1. Вы можете использовать флаг, чтобы увидеть, вызывается ли колбэк впервые, и вставлять шаблон только в том случае, если флаг еще не установлен.

customElements.define('e-candidate', class extends HTMLElement {
  connectedCallback() {
    if (!this.init) {
      var template = document.querySelector('#template-e-candidate')
      this.appendChild(template.content.cloneNode(true))
      this.querySelector('.moveUp')
          .onclick = () => 
              this.previousElementSibling && this.parentElement.insertBefore(this, this.previousElementSibling)
      this.init = true
    }
  }
})
e-candidate { display:block }
<template id="template-e-candidate">
    <slot></slot>
    <button class="moveUp">&uarr;</button>
</template>

<e-candidate>First</e-candidate>
<e-candidate>Second</e-candidate>

  1. Вы можете использовать CSS flexbox с CSS order свойство изменять порядок элементов без использования insertBefore(),
Другие вопросы по тегам