检测是否单击元素外部(必须支持单击叠加元素)

时间:2013-06-14 13:28:10

标签: javascript dom

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

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或绝对位置的所有元素。然后,如果点击外面,它会测试那些我要隐藏的elemnet元素。如果它是一个命中我计算一个新的绑定的retacle,它接受重叠边界。

它不是坚如磐石,而是有效。如果您发现以上approuch的问题,请随时发表评论

旧问题

我正在使用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;
            }
        }
    };

通过点击目标元素和文档来工作。如果单击文档但不单击目标元素,则单击其外部。这可行,但是,不适用于像datepickers等叠加项目。这是因为它们不在目标元素内,而是在体内。我能解决这个问题吗?是否有更好的方法来确定是否在元素外单击?

编辑:这种方法很有效,但前提是叠加层小于我要监视的元素

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;
        }
    }
};

我想要一个更坚固的approuch

2 个答案:

答案 0 :(得分:1)

这就是我通常解决的问题:

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
});

答案 1 :(得分:0)

我不知道您的叠加项是如何生成的,但您可以随时检查点击目标是否是要限制点击次数的元素的子项。