Javascript 手柄下拉菜单';s焦点状态

Javascript 手柄下拉菜单';s焦点状态,javascript,focus,Javascript,Focus,我目前正在用纯JavaScript制作一个下拉组件。当用户单击下拉开关时,内容将获得焦点并显示其内容。当用户在其外部单击时,内容将失去焦点并被隐藏 到目前为止,效果很好。然而,我遇到两个问题 第一种情况是,当单击下拉列表中的一个元素(例如:锚定标记)时,下拉列表会失去焦点,而这是不应该的 第二种情况是,当在显示下拉内容时单击下拉切换时,下拉列表应关闭,而不是关闭,然后由于在下拉切换上注册了单击而重新打开 HTML: JS: 我一直在想我该如何在没有任何运气的情况下解决这些问题。关于如何解决这些问

我目前正在用纯JavaScript制作一个下拉组件。当用户单击下拉开关时,内容将获得焦点并显示其内容。当用户在其外部单击时,内容将失去焦点并被隐藏

到目前为止,效果很好。然而,我遇到两个问题

第一种情况是,当单击下拉列表中的一个元素(例如:锚定标记)时,下拉列表会失去焦点,而这是不应该的

第二种情况是,当在显示下拉内容时单击下拉切换时,下拉列表应关闭,而不是关闭,然后由于在下拉切换上注册了单击而重新打开

HTML:

JS:


我一直在想我该如何在没有任何运气的情况下解决这些问题。关于如何解决这些问题有什么想法吗?

经过一些测试,我找到了一个有效的解决方案。请尝试下面的代码

        class Dropdown {

            constructor(element) {
                this.element = element;
                this.toggle = this.element.querySelector('.dropdown-toggle');
                this.content = this.element.querySelector('.dropdown-content');

                this.bindings();
            }

            bindings() {
                this.toggle.addEventListener('click', this.open.bind(this));
                this.content.addEventListener('keydown', this.handleKeyDown.bind(this));
                this.element.addEventListener('focusout', this.onFocusOut.bind(this),true);
            }
            onFocusOut(e) {
                if (!this.element.contains(e.relatedTarget)) {
                    this.close(e)
                }
            }

            open(e) {
                e.preventDefault();
                this.element.classList.toggle('is-open');
                this.content.focus();
            }

            close(e) {
                e.preventDefault();
                this.element.classList.remove('is-open');
            }

            handleKeyDown(e) {
                if (e.keyCode === 27) {
                    this.close(e);
                }
            }

        }

添加CSS可能会使问题更清楚一些。我认为您可能需要类列表上的
toggle
方法,而不是add和remove。非常感谢您的反馈。我添加了CSS。至于切换,我不是专家,但我认为使用add和remove更为明确,特别是因为它们都在不同的函数中(不像它们都在toggleDropdown方法中)。有什么特别的原因让我改为使用toggle吗?@scorchy您肯定应该至少在open函数中添加toggle,这将防止您的第二个问题,因为每次单击链接都会切换类,而不仅仅是添加类。这是你期望的行为。@ZackSunderland我刚试过,但没用。单击下拉开关时,将打开下拉列表。然后,当您单击“上一步”时,下拉列表将关闭,因为它失去了焦点(focusout listener调用close方法),但由于您单击“上一步”下拉列表切换,它会将其切换回“打开”,与classList.add的操作相同。我错过什么了吗?@scorchy你说得对。我正在看,谢谢!我刚试过,它解决了第一个问题。但是,当单击下拉开关时,它应该只关闭下拉列表,而不是重新打开它。你知道怎么解决这个问题吗?@scorchy最后一次修改,一切都对我有用。为了澄清,我刚刚更新了focusout事件,将其放在整个元素上,而不仅仅是内容上。我还将add函数更改为在open函数内部切换。如果您有任何问题,请告诉我。看起来它对我来说很好,非常感谢!我接受了你的帖子作为答案。
.dropdown {
    position: relative; 
}

.dropdown.is-open .dropdown-content {
    display: block; 
}

.dropdown-content {
    display: none;
    position: absolute;
    top: 100%;
    width: 100%;
    padding: 0.5rem 0;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); 
}
class Dropdown {

    constructor(element) {
        this.element = element;
        this.toggle = this.element.querySelector('.dropdown-toggle');
        this.content = this.element.querySelector('.dropdown-content');

        this.bindings();
    }

    bindings() {
        this.toggle.addEventListener('click', this.open.bind(this));
        this.content.addEventListener('keydown', this.handleKeyDown.bind(this));
        this.content.addEventListener('focusout', this.close.bind(this));
    }

    open(e) {
        e.preventDefault();
        this.element.classList.add('is-open');
        this.content.focus();
    }

    close(e) {
        this.element.classList.remove('is-open');
    }

    handleKeyDown(e) {
        if (e.keyCode === 27) {
            this.close(e);
        }
    }

} 

document.querySelectorAll('.dropdown').forEach(dropdown => new Dropdown(dropdown));
        class Dropdown {

            constructor(element) {
                this.element = element;
                this.toggle = this.element.querySelector('.dropdown-toggle');
                this.content = this.element.querySelector('.dropdown-content');

                this.bindings();
            }

            bindings() {
                this.toggle.addEventListener('click', this.open.bind(this));
                this.content.addEventListener('keydown', this.handleKeyDown.bind(this));
                this.element.addEventListener('focusout', this.onFocusOut.bind(this),true);
            }
            onFocusOut(e) {
                if (!this.element.contains(e.relatedTarget)) {
                    this.close(e)
                }
            }

            open(e) {
                e.preventDefault();
                this.element.classList.toggle('is-open');
                this.content.focus();
            }

            close(e) {
                e.preventDefault();
                this.element.classList.remove('is-open');
            }

            handleKeyDown(e) {
                if (e.keyCode === 27) {
                    this.close(e);
                }
            }

        }