检测是否在元素外部点击(必须支持在覆盖元素中点击)

Detect if clicking outside of element (Must support clicking in overlay elements)

本文关键字:元素 支持 覆盖 外部 是否 检测      更新时间:2023-09-26

目前我找到的最佳解决方案:

ko.bindingHandlers.clickedIn = (function () {
    function getBounds(element) {
        var pos = element.offset();
        return {
            x: pos.left,
            x2: pos.left + element.outerWidth(),
            y: pos.top,
            y2: pos.top + element.outerHeight()
        };
    }
    function hitTest(o, l) {
        function getOffset(o) {
            for (var r = { l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth, b: o.offsetHeight };
                o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
            return r.r += r.l, r.b += r.t, r;
        }
        for (var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j ? l = [l] : l).length; i;
            b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r : b.l <= a.r))
                && (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) && (r[r.length] = l[i]));
        return j ? !!r.length : r;
    }
    return {
        init: function (element, valueAccessor) {
            var target = valueAccessor();
            $(document).click(function (e) {
                if (element._clickedInElementShowing === false && target()) {
                    var $element = $(element);
                    var bounds = getBounds($element);
                    var possibleOverlays = $("[style*=z-index],[style*=absolute]").not(":hidden");
                    $.each(possibleOverlays, function () {
                        if (hitTest(element, this)) {
                            var b = getBounds($(this));
                            bounds.x = Math.min(bounds.x, b.x);
                            bounds.x2 = Math.max(bounds.x2, b.x2);
                            bounds.y = Math.min(bounds.y, b.y);
                            bounds.y2 = Math.max(bounds.y2, b.y2);
                        }
                    });

                    if (e.clientX < bounds.x || e.clientX > bounds.x2 ||
                        e.clientY < bounds.y || e.clientY > bounds.y2) {

                        target(false);
                    }
                }
                element._clickedInElementShowing = false;
            });
            $(element).click(function (e) {
                e.stopPropagation();
            });
        },
        update: function (element, valueAccessor) {
            var showing = ko.utils.unwrapObservable(valueAccessor());
            if (showing) {
                element._clickedInElementShowing = true;
            }
        }
    };
})();

首先查询所有具有z-index或绝对位置的可见元素。然后,它将这些元素与我想要隐藏的元素进行碰撞测试。如果是命中,我会计算一个新的边界,考虑到覆盖边界。

它不是坚如磐石,但有效。如果您看到上述方法的问题,请随时发表评论

老问题

我用的是Knockout但这一般适用于DOM/Javascript

我试图找到一个可靠的方法,如果检测到你点击一个元素之外。我的代码看起来像这样

    ko.bindingHandlers.clickedIn = {
        init: function (element, valueAccessor) {
            var target = valueAccessor();
            var clickedIn = false;
            ko.utils.registerEventHandler(document, "click", function (e) {
                if (!clickedIn && element._clickedInElementShowing === false) {
                    target(e.target == element);
                }
                clickedIn = false;
                element._clickedInElementShowing = false;
            });
            ko.utils.registerEventHandler(element, "click", function (e) {
                clickedIn = true;
            });
        },
        update: function (element, valueAccessor) {
            var showing = ko.utils.unwrapObservable(valueAccessor());
            if (showing) {
                element._clickedInElementShowing = true;
            }
        }
    };

它的工作原理是同时监听点击目标元素和文档。如果你点击文档而不是目标元素,你就点击它的外部。这是有效的,但是,不是覆盖项目,如日期选择器等。这是因为它们不在目标元素中,而是在主体中。我能解决这个问题吗?是否有更好的方法来确定是否在元素外部点击?

edit:这种方法有效,但只有当覆盖层小于我想要监视的元素

ko.bindingHandlers.clickedIn = {
    init: function (element, valueAccessor) {
        var target = valueAccessor();
        $(document).click(function (e) {
            if (element._clickedInElementShowing === false) {
                var $element = $(element);
                var pos = $element.offset();
                if (e.clientX < pos.left || e.clientX > (pos.left + $element.width()) ||
                    e.clientY < pos.top || e.clientY > (pos.top + $element.height())) {
                    target(false);
                }
            }
            element._clickedInElementShowing = false;
        });
        $(element).click(function (e) {
            e.stopPropagation();
        });
    },
    update: function (element, valueAccessor) {
        var showing = ko.utils.unwrapObservable(valueAccessor());
        if (showing) {
            element._clickedInElementShowing = true;
        }
    }
};

我想要一个更可靠的方法

我通常是这样解决的:

http://jsfiddle.net/jonigiuro/KLxnV/

$('.container').on('click', function(e) {
    alert('hide the child');
});
$('.child').on('click', function(e) {
    alert('do nothing');
    e.stopPropagation(); //THIS IS THE IMPORTANT PART
});

我不知道你的叠加项是如何生成的,但你总是可以检查点击目标是否是你想要限制点击的元素的子元素。