将选择框包装成自定义元素。

问题描述 投票:0回答:1

我想建立一个HTML自定义元素,它的行为应该和本地的元素几乎一样。<select> 元素,但除此之外,每次属性或子节点发生变化时,它还必须调用一定的更新函数。(后面的故事。这是在使用 靴带选择 元素,在elm框架内。另见 最后一个问题.)

使用LitElement框架,我能够建立一个有效的自定义元素(称为 <lit-select>)类似于上面的描述。但遗憾的是,我没能让它接受 html <option><optgroup> 元素作为子元素。取而代之的是,用户必须将选项列表作为json编码的字符串传递给某个属性。

也就是说,不需要调用

<lit-select>
  <option>foo</option>
  <option>bar</option>
</lit-select>

用户必须调用

<lit-select items='["foo", "bar"]'></lit-select>

这样一来,我就得改变我的定义了。<lit-select> 以使第一次通话成为可能?我是知道的。<slot> 元素,但不幸的是,这个元素不允许在 <select>所以浏览器就直接删除了。

先谢谢你


更新1

其实有一些限制使得这个问题比我一开始想象的更有挑战性。

  • 我必须避免使用影子DOM,这是因为我的自定义元素被bootstrap(和bootstrap-select)的cssjs 这是因为我的自定义元素被bootstrap(和bootstrap-select)的cssjs增强了样式,而cssjs只看普通DOM。正如我刚刚了解到的,这就排除了 slot 元素,因为这些都是影子DOM特定的。
  • 我的自定义元素必须完全响应变化(添加和删除子注释,改变属性)。这是因为我计划在虚拟DOM中使用该元素(在我的例子中是elm,但它也应该适用于反应)。

附录

我的定义是 <lit-select>:

import { LitElement, html, customElement, property } from 'lit-element';
import * as $ from 'jquery';
import 'bootstrap';
import 'bootstrap-select';

@customElement('lit-select')
export class LitSelect extends LitElement {

    @property({ type : Array }) items = []

    updated() {
        $(this).find(".selectpicker").selectpicker('refresh');
    }

    createRenderRoot() {
        return this;
    }

    private renderItem(item: string) {
        return html`
            <option>
                ${item}
            </option>
        `;
    }

    render() {
        return html`
            <select class="selectpicker" data-live-search = "true">
                ${this.items.map(item => this.renderItem(item))}
            </select>
        `;
    }
}
javascript typescript polymer custom-element lit-element
1个回答
0
投票

为什么不从一个 原生的W3C标准自定义元素

你要做的就是 移动 一些 <option> 元素。

<template id=MY-SELECT>
  <h3>My Never Todo List</h3>
  <select multiple></select>
</template>

<my-select name="NTD">
  <option>Grow up</option>
  <option>Learn React</option>
  <option>Use Lit</option>
  <option>Forget W3C Custom Elements API</option>
</my-select>

<script>
  customElements.define("my-select", class extends HTMLElement {
    static get observedAttributes() {
      return ["name"]; //use one to trigger attributeChangedCallback
    }
    connectedCallback() {
      console.log('connected');
      //clone template
      this.append(document.getElementById(this.nodeName).content.cloneNode(true));
      //MOVE options inside SELECT:
      this.querySelector('select').append(...this.querySelectorAll('option'));
    }
    attributeChangedCallback() {
      setTimeout(() => this.updated(...arguments))// execute after Event Loop is done
    }
    updated(name,oldValue,newValue){
      console.log('updated',name,oldValue,newValue);
    }
  })

</script>

0
投票

这个解决方案如何?

import {LitElement, html } from 'lit-element';


export class GraniteSelect extends LitElement {

  static get properties() {
    return {
      options: {
        type: Object,
      }
    }
  }

  constructor() {
    super();
    this.options =  [];
  }

  connectedCallback() {
    super.connectedCallback();

    this.observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        mutation.removedNodes.forEach((node) => {
          if (node.nodeName === 'OPTION' ) {
            this.options = [ ...this.options, node ];
           console.log(`options - ${this.options}`);
           console.dir(this.options);
          }
        });
      });
    });
    this.observer.observe(this, {
        childList: true,
    });
  }

  firstUpdate() {
    this.observer.disconnect();
  }

  createRenderRoot() {
  /**
   * Render template without shadow DOM. Note that shadow DOM features like 
   * encapsulated CSS and slots are unavailable.
   */
    return this;
  }


  render() {
    console.log('Rendering', this.options);
    return html`
      <select class="selectpicker" data-live-search = "true">
          ${this.options}
      </select>
    `;
  }
}

window.customElements.define('granite-select', GraniteSelect);

它使用了light-dom(即 createShadowRoot 部分),并且它将你的元素的所有选项子代放在 select. 非选项的子代会被忽略,但你可以对它们做任何事情。

你可以在 https:/stackblitz.comeditjs-14dcae。

你对这种做法怎么看?

© www.soinside.com 2019 - 2024. All rights reserved.