CSS / JS - отображать фокус на элементе, отличном от сфокусированного - PullRequest
0 голосов
/ 18 апреля 2020

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

<div class="parent">
  <div class="other-elements>
    <span>abc</span>
    <span>def</span>
  </div>
  <input type="text" class="child">
</div>

Теперь у меня есть следующее css, чтобы в основном поменять вид дочернего элемента и родителя:

.parent {
  -moz-appearance: textfield;
  -webkit-appearance: textfield;
  display: flex;
  flex-direction: row;
}

.child {
  border: none;
  flex: 1;
}

Теперь, так как ввод больше не имеет границы, фокус не будет отображаться. Поэтому я хочу вместо этого отобразить фокус на parent. Насколько я узнал, подделка псевдокласса focus на parent невозможна. Я предполагаю, что решение вместо этого было бы сфокусировать parent, как только child получит фокус, и передать все события клавиш и мыши. Тем не менее, это не кажется мне очень приятным. Хотя изменение границы parent на focus / blur также возможно, я бы хотел не слишком сильно менять стиль браузера по умолчанию.

Вот пользовательский компонент, который я написал:

class MultiItemPicker extends HTMLElement {
    constructor() {
        super();
        const shadow = this.attachShadow({mode: 'open'});
        this.classList.add('multi-item-picker');

        const style = document.createElement("style")
        style.textContent = `
        .multi-item-picker {
            -moz-appearance: textfield;
            -webkit-appearance: textfield;
            display: flex;
            flex-direction: row;
            padding: 5px 0 5px 0;
        }

        .multi-item-picker-input {
            border: none;
            flex: 1;
        }

        .multi-item-picker-selected-items {
            display: flex;
            flex-direction: row;
            flex: 0;
        }

        .multi-item-picker-selected-item {
            background-color: lightgray;
            padding: 2px 10px 2px 10px;
            /* We don't want the items growing inside of the parent, as the text input should be the only growing components.*/
            flex: 0;
        }`

        shadow.appendChild(style);

        const wrapper = document.createElement('div');
        wrapper.classList.add('multi-item-picker');
        shadow.appendChild(wrapper)

        const selectedItemsWrapper = document.createElement('div');
        selectedItemsWrapper.classList.add('multi-item-picker-selected-items');
        wrapper.appendChild(selectedItemsWrapper);

        const input = document.createElement('input');
        input.type = "text";
        input.classList.add('multi-item-picker-input');
        input.value = "test";

        const demoValues = ["abc", "def", "ghi"]

        input.addEventListener("input", function () {
            for (let i = 0; i < demoValues.length; i++) {
                let demoValue = demoValues[i];
                if (input.value === demoValue) {
                    const newSelectedItem = document.createElement("span");
                    newSelectedItem.classList.add("multi-item-picker-selected-item");
                    newSelectedItem.innerText = demoValue;
                    selectedItemsWrapper.appendChild(newSelectedItem);
                    input.value = "";
                    break;
                }
            }
        }, false)

        input.addEventListener("keydown", function (event) {
            if (input.value === "" && event.code === "Backspace" && selectedItemsWrapper.childElementCount >= 1) {
                input.value = selectedItemsWrapper.lastChild.textContent;
                selectedItemsWrapper.removeChild(selectedItemsWrapper.lastChild);
            }
        }, false);

        wrapper.appendChild(input);
    }
}

customElements.define('multi-item-picker', MultiItemPicker);

Обратите внимание, что я не использую никаких фреймворков / библиотек, это все ванильно.

...