web-dev-qa-db-ja.com

ネイティブ形式のカスタム入力要素

Webコンポーネントの場合、人々が最も作成してオーバーライドしたい要素の1つは<input>です。入力要素は、タイプによって多くのものがあり、通常はカスタマイズが難しいため、悪いものです。そのため、人々は常に外観や動作を変更したいと思うのが普通です。

2年前、多かれ少なかれ、Webコンポーネントについて最初に聞いたとき、私はかなり興奮していました。私が作成したいと思った最初の種類の要素は、カスタム入力要素でした。仕様が完成したので、入力要素の必要性は解決されていないようです。 Shadow DOMを使用すると、内部構造と外観を変更できるはずでしたが、入力要素はブラックリストに登録されており、既に非表示になっているため、シャドウルートを設定できません。ロジックと動作を追加したい場合は、is属性を持つカスタムの組み込み要素でうまくいくはずです。 Shadow DOMマジックを実行することはできませんが、少なくともこれはありますよね?さてSafariはそれを実装するつもりはありません、polymerは、まもなく廃止される予定の標準のようなにおいがするその理由でそれらを使用しません。

したがって、通常のカスタム要素が残ります。彼らはシャドウDOMを使用して、私が望むどんなロジックでも持つことができますが、私はそれらを入力にしたいのです!それらは<form>内で機能するはずですが、私が正しければ、フォーム要素はそれらを好みません。ネイティブのフォーム要素のすべてを複製する独自のカスタムフォーム要素も作成する必要がありますか? FormData、検証APIなどに別れを告げる必要がありますか? javascriptなしで機能する入力を持つフォームを持つ機能を失いますか?

19
olanod

必要な外観と動作でカスタム要素を作成できます。

その中に、右側のnameを持つ非表示の<input>要素を配置します(これは<form>に渡されます)。

カスタム要素「visiblevalue」が変更されるたびに、そのvalue属性を更新します。

同様のSO質問 に対するこの回答)に例を投稿しました。

class CI extends HTMLElement 
{
    constructor ()
    {
        super()
        var sh = this.attachShadow( { mode: 'open' } )
        sh.appendChild( tpl.content.cloneNode( true ) )
    }

    connectedCallback ()
    {
        var view = this
        var name = this.getAttribute( 'name' )

        //proxy input elemnt
        var input = document.createElement( 'input' )
        input.name = name
        input.value = this.getAttribute( 'value' )
        input.id = 'realInput'
        input.style = 'width:0;height:0;border:none;background:red'
        input.tabIndex = -1
        this.appendChild( input )


        //content editable
        var content = this.shadowRoot.querySelector( '#content' )
        content.textContent = this.getAttribute( 'value' )
        content.oninput = function ()
        {
            //console.warn( 'content editable changed to', content.textContent )
            view.setAttribute( 'value', content.textContent)
        }

        //click on label
        var label = document.querySelector( 'label[for="' + name + '"]' )
        label.onclick = function () { content.focus() }

        //autofill update
        input.addEventListener( 'change', function ()
        {
            //console.warn( 'real input changed' )
            view.setAttribute( 'value', this.value )
            content.value = this.value 
        } )

        this.connected = true 
    }

    attributeChangedCallback ( name, old, value )
    {
        //console.info( 'attribute %s changed to %s', name, value )
        if ( this.connected )
        {
            this.querySelector( '#realInput' ).value = value 
            this.shadowRoot.querySelector( '#content' ).textContent = value 
        }                
    }

}
CI.observedAttributes = [ "value" ]
customElements.define( 'custom-input', CI )
//Submit
function submitF ()
{
    for( var i = 0 ; i < this.length ; i++ )
    {
        var input = this[i]
        if ( input.name ) console.log( '%s=%s', input.name, input.value )
    } 
}
S1.onclick = function () { submitF.apply(form1) }
<form id=form1>
    <table>
        <tr><td><label for=name>Name</label>        <td><input name=name id=name>
        <tr><td><label for=address>Address</label>  <td><input name=address id=address>
        <tr><td><label for=city>City</label>        <td><custom-input id=city name=city></custom-input>
        <tr><td><label for=Zip>Zip</label>          <td><input name=Zip id=Zip>
        <tr><td colspan=2><input id=S1 type=button value="Submit">
    </table>
</form>
<hr>
<div>
  <button onclick="document.querySelector('custom-input').setAttribute('value','Paris')">city => Paris</button>
</div>

<template id=tpl>
  <style>
    #content {
      background: dodgerblue;
      color: white;
      min-width: 50px;
      font-family: Courier New, Courier, monospace;
      font-size: 1.3em;
      font-weight: 600;
      display: inline-block;
      padding: 2px;
    }
  </style>
  <div contenteditable id=content></div>
  <slot></slot>
</template>
12
Supersharp