简体   繁体   中英

How can I detect when a user touches something other than a menu or it's children?

I'm trying to write some JavaScript to handle touch interactions on a drop down menu. This is working well, except that I can't figure out how to hide the menu when a user taps away from it.

My script is as follows:

// mark all menu items as inative
function mark_all_inactive(elem) {
    elem.find(".is-active").removeClass("is-active");
    elem.find("[aria-hidden=false]").attr("aria-hidden", "true");
}

// mark element as active
function mark_active(elem) {
    elem.closest(".menu-list_item").addClass("is-active");
    elem.siblings("[aria-hidden]").attr("aria-hidden", "false");
}

// open on touchstart
jQuery(".menu-list_item").not(".menu-list_item.-mega .menu-list_item.-parent").on("touchstart", function(e) {
    if (!jQuery(this).hasClass("is-active") && jQuery(this).children("[aria-hidden]").length) {
        e.preventDefault();
        mark_all_inactive(jQuery(this).closest(".menu-list"));
        mark_active(jQuery(this).children(".menu-list_link"));
    }
});

// hide on focusout
jQuery(".menu-list").on("focusout", ".menu-list_link", function() {
    var parent_item = jQuery(this).closest(".menu-list_item.-parent.is-active");

    if (parent_item.length) {
        // timeout required for the next element to get focus
        setTimeout(function() {
            if (!parent_item.find(":focus").length) {
                parent_item.removeClass("is-active");
                parent_item.children("[aria-hidden]").attr("aria-hidden", "true");
                parent_item.closest(".menu-list_item.-parent.is-active").find(".menu-list_link").first().trigger("focusout");
            }
        }, 10);
    }
});

As you can see, I'm using touchstart to add some classes, which works perfectly. The issue arises when trying to remove those classes.

The focusout bit of code is primarily used for keyboard navigation, but I'd like to adapt it to also trigger when you tap away from the menu.

From what I understand, mobile browsers typically don't fire focusout when tapping away from something. That seems odd to me, as you'd think tapping away from something would surly fire a focusout regardless of input type, but that's beside the point.

I have looked in to touchend , but that appears to fire immediately after lifting your finger, which isn't what I want. The goal is to tap once to add the classes, then be able to lift your finger to select a child link (which can also contain flyout menus, thus re-using the same touchstart script). Then, when you tap anywhere that's not the active menu, remove the classes.

So, on to the question: Can the focusout bit of script be modified to accommodate tapping away from a menu or it's children? If not, how would I go about writing a second bit of script to accomplish that task?

You can view a live demo at the follow URL:

https://framework.ghostlyco.de/

I figured this out, but I don't know that it's the best way. It seems kind of dumb to attach an event to the entire document, but this works:

// hide on touch away
jQuery(document).on("touchstart", function(e) {
    if (!jQuery(e.target).closest(".menu-list_item.-parent.is-active").length) {
        jQuery(".menu-list_item.-parent.is-active").children("[aria-hidden]").attr("aria-hidden", "true");
        jQuery(".menu-list_item.-parent.is-active").removeClass("is-active");
    }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM