【问题标题】:Conflict between focusout and click events in JSJS中focusout和click事件的冲突
【发布时间】:2020-08-04 23:13:37
【问题描述】:

我需要一些帮助来找出解决 focusoutclick 事件冲突的方法。

我正在做一个dropdownlist 项目,所以我们有两个下拉列表:

  1. 如果click在箭头按钮上,这个打开了一个列表。但是,当您在箭头按钮上click 时,这个应该会关闭列表。

  2. 如果click 在文本框上,这个打开了一个列表。但是,当您在文本框上click 时,这个应该会关闭列表。

我遇到的问题是,当您尝试按箭头按钮关闭列表时,文本框的focusout 事件。它正在打开,关闭,然后再次打开。

所以它应该在需要时立即关闭,如果列表打开,则何时关闭。

PS:删除按钮还没有功能。

我该怎么办?

"use strict";

// LISTA DE INICIALIZACIÓN DE VARIABLES

// Se obtiene los ID de los elementos principales.

const tagSys = document.getElementById('tag-sys');
const tagInput = document.getElementById('input-section');
const textInput = document.getElementById('text-input');
const arrowButton = document.getElementById('btn-arrow');
var displayToggle = false;

// Se crea un arreglo por default.

var data = [
    'The Shawshank Redemption',
    'The Godfather',
    'The Godfather: Part II',
    'The Dark Knight',
    '12 Angry Men',
    "Schindler's List",
    'Pulp Fiction',
    'The Lord of the Rings: The Return of the King',
    'The Good, the Bad and the Ugly',
    'Fight Club'
];

// LISTA DE EVENTOS ACONTINUACIÓN

/* 
    El siguiente addEventListener se aplica sobre textInput.
    Su función hace mostrar el listado de data segun las palabras
    ingresadas.
*/

textInput.addEventListener('keyup', function (e) {
    console.log('keyup');
    let hasData = DropdownFilter(e.target.value);
    DropdownDisplay(hasData);
});

/* 
    El siguiente addEventListener se aplica sobre textInput.
    Su función hace mostrar el listado de la data.
*/

textInput.addEventListener('click', function () {
    DisplayToggle();
});

/* 
    El siguiente addEventListener se aplica sobre textInput.
    Su función hace cerrar el listado de la data.
*/

textInput.addEventListener('focusout', function () {
    if (displayToggle == true) {
        let hasData = false;
        DropdownDisplay(hasData);
        displayToggle = false;
    }
});

/* 
    El siguiente addEventListener se aplica sobre arrowButton.
    Su función hace enviar el focus al textInput y mostrar
    el listado de la data.
*/

arrowButton.addEventListener('click', function () {
    textInput.focus();
    DisplayToggle();
});

// LISTA DE MÉTODOS ACONTINUACIÓN

function DisplayToggle() {
    if (displayToggle == false) {
        let hasData = DropdownList(data);
        DropdownDisplay(hasData);
        displayToggle = true;
    } else {
        let hasData = false;
        DropdownDisplay(hasData);
        displayToggle = false;
    }
}

/* 
    El siguiente método DropdrowFilter, recibe como parametro
    una palabra que se encarga de buscar dentro del arreglo,
    para luego enviar al metodo DropdownList.
*/

function DropdownFilter(params) {
    let dataFilter = data.filter(function (e) {
        return e.toLowerCase().includes(params.toLowerCase());
    });
    return DropdownList(dataFilter);
}

/* 
    El siguiente método DropdrowList, recibe como parametro
    una lista de datos que usara para mostrar.
*/

function DropdownList(dataList) {
    RemoveList();
    if (dataList.length != 0) {
        let objList = {
            div: document.createElement('div'),
            ul: document.createElement('ul'),
            li: ''
        };

        objList.div.setAttribute("id", "tag-list");
        objList.div.classList.add('tag-list');
        tagInput.appendChild(objList.div);

        objList.div.appendChild(objList.ul);

        dataList.forEach(element => {
            objList.li = document.createElement('li');
            objList.li.textContent = element;
            objList.ul.appendChild(objList.li);
        });
    } else {
        let objList = {
            div: document.createElement('div'),
            ul: document.createElement('ul'),
            li: document.createElement('li')
        };

        objList.div.setAttribute("id", "tag-list");
        objList.div.classList.add('tag-list');
        tagInput.appendChild(objList.div);

        objList.div.appendChild(objList.ul);

        dataList[0] = 'No options';
        objList.li.textContent = dataList[0];
        objList.ul.appendChild(objList.li);
    }

    return true; // Retornamos True porque ya retorna data para mostrar.
}

/* 
    El siguiente método RemoveList, cumple como función
    eliminar la etiqueta con ID 'tag-list'.
*/

function RemoveList() {
    if (document.getElementById('tag-list')) {
        let div = document.getElementById('tag-list');
        tagInput.removeChild(div);
    }
}

/* 
    El siguiente metodo DropdownDisplay, cumple como función
    agregar la clase 'tag-list-show' para que sea visible la data,
    y la posición de la flecha.
*/

function DropdownDisplay(params) {
    let div = document.getElementById('tag-list');
    if (params == true) {
        div.classList.add('tag-list-show');
        arrowButton.classList.replace('fa-chevron-down', 'fa-chevron-up');
    } else {
        div.classList.remove('tag-list-show');
        arrowButton.classList.replace('fa-chevron-up', 'fa-chevron-down');
    }
}
body {
    margin: 0;
    background-color: #f39c12;
    font-family: 'Poppins', sans-serif;
}

main {
    align-items: center;
    display: flex;
    height: 100vh;
    justify-content: center;
}

.tag-sys {
    border: 1px solid #000000;
    border-radius: 2px;
    cursor: text;
    display: flex;
    font-size: 13px;
    max-width: 200px;
    min-height: 20px;
    position: relative;
}

.input-section {
    display: flex;
    flex-wrap: wrap;
    width: 80%;
}

.text-input {
    border: none;
    border-right: 1px solid #000000;
    border-bottom-left-radius: 2px;
    border-top-left-radius: 2px;
    font-size: 1em;
    flex: 1;
    margin: 0;
    min-width: 20px;
    padding: 0;
    padding-left: 2px;
}

.text-input::placeholder {
    color: #bdc3c7;
}

.text-input:focus {
    outline: none;
}

.control-section {
    align-items: center;
    background-color: #bdc3c7;
    display: flex;
    justify-content: space-around;
    width: 20%;
}

.control-section-icon {
    align-items: center;
    border-radius: 3px;
    cursor: pointer;
    display: flex;
    height: 16px;
    justify-content: center;
    width: 16px;
}

.control-section-icon i {
    font-size: 14px;
}

.tag-list {
    background-color: #ffffff;
    border: 1px solid #000000;
    border-radius: 2px;
    position: absolute;
    font-size: 14px;
    height: 100px;
    overflow-y: scroll;
    left: -1px;
    top: 22px;
    width: 100%;
    display: none;
}

.tag-list-show {
    display: block !important;
}

.tag-list ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
}

.tag-list ul li {
    padding: 5px;
}

.tag-list ul li:hover {
    cursor: pointer;
    background-color: #bdc3c7;
}
<link rel="stylesheet" type="text/css" href="style.css">
    <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet"> 
    <script src="https://kit.fontawesome.com/1001c03ba9.js" crossorigin="anonymous"></script>
    <main>
        <div id="tag-sys" class="tag-sys">
            <div id="input-section" class="input-section">
                <input id="text-input" class="text-input" type="text">
            </div>
            <div class="control-section">
                <span class="control-section-icon">
                    <i id="btn-delete" class="fas fa-times"></i>
                </span>
                <span class="control-section-icon">
                    <i id="btn-arrow" class="fas fa-chevron-down"></i>
                </span>
            </div>
        </div>
    </main>
    <script src="app.js"></script>

【问题讨论】:

    标签: javascript dom-events addeventlistener


    【解决方案1】:

    这是因为focusoutclick 之前触发。请改用mousedown

    【讨论】:

    • 我试过了,但没有结果。我首先移动了click 事件而不是focusout,我得到了相同的结果。我将click 更改为mousedown,什么也没有。
    • 好的,我进一步调查了。发生这种情况是因为您在 click/mousdown 上有 .focus。因此,通过点击您会触发focusout-&gt;click,因此您可以非常快速地隐藏显示列表。那是最初的问题。但是第一个简单的解决方案是没有意义的,因为使用 mousedown 你会得到mousedown-&gt;focusin-&gt;focusout。 mousedown 触发关注文本字段的功能,mousedown 移除焦点后触发 mouseup 并触发隐藏。
    • 是的,我看到了,它有点像聚焦和点击之间的循环。你给我推荐什么?我想到了另一种解决方案,点击arrowButton时不要聚焦inputText,但该解决方案给我带来了另一个问题,如果我不能在arrowButton上使用focusout,如何关闭列表。
    • 如果目标不是文本字段,我建议尝试使用其他方式关闭外部菜单,例如 click 用于文档。 stackoverflow.com/questions/6463486/… 喜欢这里,但选择的答案并不理想,因为它使用 stopPropagation 在某些情况下可能会出现问题。虽然如果对你来说它工作得很好,那没关系。
    • 那个链接帮助我找到了新的东西,它是Bubbling。另外,我在document 上使用了addEveneListener,但它发生了同样的问题,所以我在其他arrowButton 和inputButton addEventListener 上使用了stopPropagation,它按我的预期工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-05
    • 2019-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多