私が見つけた現在の最良の解決策:
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 または絶対位置のいずれかを持つすべての要素に対する最初のクエリで機能します。次に、外側をクリックすると非表示にする要素に対してこれらの要素をヒットテストします。ヒットした場合は、オーバーレイ境界を考慮した新しい境界 retacle を計算します。
堅実ではありませんが、機能します。上記のアプローチで問題が発生した場合は、お気軽にコメントしてください
古い質問
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;
}
}
};
もっと堅実なアプローチが欲しい