Пользовательский тег выбора

Я хочу создать пользовательский тег выбора, например, и я хочу, чтобы он наследовал все атрибуты. Я старался:

document.registerElement('my-select', {
    prototype: Object.create(HTMLSelectElement.prototype),
    extends: 'select'
});

document.registerElement('my-option', {
    prototype: Object.create(HTMLOptionElement.prototype),
    extends: 'option'
});

Но, похоже, это не работает. Что я сделал не так?

2 ответа

У вас есть только абстрактный способ создания настраиваемого компонента select. Реализация будет включать создание двух прототипов, а именно, для select и option, и, наконец, подключение его к настраиваемому полю выбора, которое мы объявим на HTML-странице.

Закажите эту ссылку для демонстрации: https://jsfiddle.net/47gzo8kt/

Javascript:

var CustomizedSelectOptionPrototype = Object.create(HTMLElement.prototype);
document.registerElement('cust-select-option', { prototype: CustomizedSelectOptionPrototype});

var CustomizedSelectProto = Object.create(HTMLSelectElement.prototype);
CustomizedSelectProto.createdCallback = function() {
    if (!this.getAttribute('tabindex')) {
        this.setAttribute('tabindex', 0);
    }
    this.placeholder = document.createElement('span');
    this.appendChild(this.placeholder);

    var selected = this.querySelector('cust-select-option[selected]');
    this.placeholder.textContent = selected ? selected.textContent : (this.getAttribute('placeholder') || '');
};
document.registerElement('cust-select', { prototype: CustomizedSelectProto, extends:"select"});

HTML:

<label>
    Customized Select Box:
    <select is="cust-select" placeholder="Please select an option">
        <option selected value="1">English</option>
        <option value="2">French</option>
        <option value="3">Hindi</option>
    </select>
</label>

Прошло некоторое время с тех пор, как был задан этот вопрос. Однако я столкнулся с этим, пытаясь сделать то же самое. Кажется, спецификации v0 и v1 сейчас беспорядок. Несмотря на использование того же кода для HTMLButtonElement, он бросает недопустимый конструктор в chrome, так же, как и использование HTMLSelectElement. Я начал писать это и думал, что поделюсь. Он может быть очищен и добавлено больше методов select element, но подумал, что другие могут взять его отсюда, чтобы адаптировать его к своим потребностям.

class mySelectElement extends HTMLElement {
    static get observedAttributes() { return ['disabled']; }
    constructor() {
        super();
        let shadowRoot = this.attachShadow({
                mode: 'open'
            }),
            content = document.createElement('slot'),
            options = null;
        content.setAttribute('select', 'option');
        shadowRoot.innerHTML = `<style>
:host([disabled]) {
background: grey;
pointer-events: none;
opacity: 0.4;
pointer-events: none;
height: 16px;
}
:host:before{
content: '';
}
:host{
contain: layout size style;
overflow: auto;
align-items:center;
background-color:rgb(255, 255, 255);
border: 1px solid black;
color:rgb(0, 0, 0);
display:inline-block;
font: 13.3333px Arial;
height:16px;
width:145px;
writing-mode:horizontal-tb;
-webkit-appearance:menulist;
-webkit-rtl-ordering:logical;
}
.hide{
display:none;
}
#options{
position: fixed;
border:1px solid blue;
}       
::slotted(option){
background-color:white;
}
::slotted(:hover){
background-color: #a4d8d2;
}
</style>
<div id="options" class="hide"></div>`;
        options = shadowRoot.getElementById('options');
        options.appendChild(content);
        this.disabled = false;
        this.setAttribute('tabIndex', -1);
        this.addEventListener('click', function (e) {
            let target = e.target;
            if (target.nodeName == 'OPTION') {
                this.value = target.value;
                Array.from(target.parentElement.children).forEach(x => x.removeAttribute('selected'));
                target.setAttribute('selected', '');
                shadowRoot.styleSheets[0].rules[1].style.cssText = "content: " 
                    + '"' + target.textContent + '"';
                this.blur();
            }
        });
        this.addEventListener('focus', function () {
            let rect = this.getBoundingClientRect();
            options.style.top = rect.bottom;
            options.style.left = rect.left;
            options.style.width = rect.width;
            options.classList.remove('hide');
        });
        this.addEventListener('focusout', function () {
            options.classList.add('hide');
        });
        this.add = function (item) {
            this.appendChild(item);
            if (this.value == undefined) {
                this.value = content.assignedNodes()[0].value;
                content.assignedNodes()[0].setAttribute('selected', '');
                shadowRoot.styleSheets[0].rules[1].style.cssText = "content: " +
                    '"' + content.assignedNodes()[0].textContent + '"';
            }
        }
        this.item = function (i) {
            return content.assignedNodes()[i];
        }
        this.namedItem = function (val) {
            return content.assignedNodes().find(x => x.value == val);
        }
        this.remove = function (i) {
            return content.assignedNodes()[i].remove();
        }
    }
    attributeChangedCallback(attributeName, oldValue, newValue, namespace) {
        if (attributeName == 'disabled') {
            if (newValue = '')
                this.disabled = true;
            else if (newValue == null)
                this.disabled = false;
        }

    }
}

customElements.define('my-select', mySelectElement);
var _select = customElements.get('my-select');

var select = new _select;
document.body.appendChild(select);
for (let i = 0; i < 10; i++) {
    let option = document.createElement('option');
    option.innerHTML = 'hello_' + i;
    option.value = 'h' + i;
    select.add(option);
}
Другие вопросы по тегам