Запретить onmouseout при наведении дочернего элемента родительского абсолютного div БЕЗ jQuery
У меня возникают проблемы с функцией onmouseout
в абсолютном позиционированном div. Когда мышь попадает в дочерний элемент в div, событие mouseout срабатывает, но я не хочу, чтобы он срабатывал до тех пор, пока мышь не вышла из родителя, абсолютный div.
Как я могу предотвратить запуск события mouseout
при попадании в дочерний элемент WITHOUT jQuery.
Я знаю, что это связано с бурлящим событием, но мне не повезло узнать, как это сделать.
Я нашел аналогичную запись здесь: Как отключить события мыши, вызванные дочерними элементами?
Однако это решение использует jQuery.
Ответы
Ответ 1
function onMouseOut(event) {
//this is the original element the event handler was assigned to
var e = event.toElement || event.relatedTarget;
if (e.parentNode == this || e == this) {
return;
}
alert('MouseOut');
// handle mouse event here!
}
document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
Я сделал быструю демонстрацию JsFiddle, со всеми необходимыми CSS и HTML, проверьте это...
EDIT ФИКСИРОВАННАЯ ссылка для поддержки кросс-браузера http://jsfiddle.net/RH3tA/9/
ПРИМЕЧАНИЕ, что это проверяет только непосредственного родителя, если родительский div имеет вложенные дочерние элементы, то вам нужно каким-то образом пройти через элементы, которые ищут "Элемент Orginal"
EDIT пример для вложенных детей
EDIT Исправлено для многопользовательского браузера
function makeMouseOutFn(elem){
var list = traverseChildren(elem);
return function onMouseOut(event) {
var e = event.toElement || event.relatedTarget;
if (!!~list.indexOf(e)) {
return;
}
alert('MouseOut');
// handle mouse event here!
};
}
//using closure to cache all child elements
var parent = document.getElementById("parent");
parent.addEventListener('mouseout',makeMouseOutFn(parent),true);
//quick and dirty DFS children traversal,
function traverseChildren(elem){
var children = [];
var q = [];
q.push(elem);
while (q.length > 0) {
var elem = q.pop();
children.push(elem);
pushAll(elem.children);
}
function pushAll(elemArray){
for(var i=0; i < elemArray.length; i++) {
q.push(elemArray[i]);
}
}
return children;
}
И новая JSFiddle, EDIT обновленная ссылка
Ответ 2
Используйте onmouseleave
.
Или, в jQuery, используйте mouseleave()
Это точная вещь, которую вы ищете. Пример:
<div class="outer" onmouseleave="yourFunction()">
<div class="inner">
</div>
</div>
или, в jQuery:
$(".outer").mouseleave(function(){
//your code here
});
пример здесь.
Ответ 3
Для более простого чистого CSS-решения, которое работает в некоторых случаях (IE11 или новее), можно удалить детей pointer-events
, установив их в none
.parent * {
pointer-events: none;
}
Ответ 4
Вот более элегантное решение, основанное на том, что было ниже.
это связано с тем, что события выходят из более чем одного уровня детей.
Это также учитывает проблемы с несколькими браузерами.
function onMouseOut(this, event) {
//this is the original element the event handler was assigned to
var e = event.toElement || event.relatedTarget;
//check for all children levels (checking from bottom up)
while(e && e.parentNode && e.parentNode != window) {
if (e.parentNode == this|| e == this) {
if(e.preventDefault) e.preventDefault();
return false;
}
e = e.parentNode;
}
//Do something u need here
}
document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
Ответ 5
Благодаря Амджаду Масаду, который вдохновил меня.
У меня есть следующее решение, которое, похоже, работает в IE9, FF и Chrome, а код довольно короткий (без сложного закрытия и поперечных дочерних элементов):
DIV.onmouseout=function(e){
// check and loop relatedTarget.parentNode
// ignore event triggered mouse overing any child element or leaving itself
var obj=e.relatedTarget;
while(obj!=null){
if(obj==this){
return;
}
obj=obj.parentNode;
}
// now perform the actual action you want to do only when mouse is leaving the DIV
}
Ответ 6
Если вы используете jQuery, вы также можете использовать функцию "mouseleave", которая касается всего этого для вас.
$('#thetargetdiv').mouseenter(do_something);
$('#thetargetdiv').mouseleave(do_something_else);
do_something будет срабатывать, когда мышь войдет в thetargetdiv или любой из его детей, do_something_else будет срабатывать только тогда, когда мышь покинет thetargetdiv и любой из его детей.
Ответ 7
Попробуйте mouseleave()
Пример:
<div id="parent" mouseleave="function">
<div id="child">
</div>
</div>
;)
Ответ 8
Я думаю, Quirksmode имеет все необходимые вам ответы (поведение браузеров в разных браузерах и события mouseenter/mouseleave), но я думаю, что наиболее распространенным выводом для этого события барбинг-барда является использование фреймворка вроде JQuery или Mootools (с событиями mouseenter и mouseleave, которые именно то, что вы интуитивно поняли).
Посмотрите, как они это делают, если хотите, сделайте это самостоятельно
или вы можете создать свою собственную скудную среднюю версию Mootools только с частью события (и ее зависимостями).
Ответ 9
Я нашел очень простое решение,
просто используйте событие onmouseleave = "myfunc()", чем событие onmousout = "myfunc()"
В моем коде это сработало!
Пример:
<html>
<head>
<script type="text/javascript">
function myFunc(){
document.getElementById('hide_div').style.display = 'none';
}
function ShowFunc(){
document.getElementById('hide_div').style.display = 'block';
}
</script>
</head>
<body>
<div onmouseleave="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
Hover mouse here
<div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
CHILD <br/> It doesn't fires if you hover mouse over this child_div
</div>
</div>
<div id="hide_div" >TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>
Тот же пример с функцией мыши:
<html>
<head>
<script type="text/javascript">
function myFunc(){
document.getElementById('hide_div').style.display = 'none';
}
function ShowFunc(){
document.getElementById('hide_div').style.display = 'block';
}
</script>
</head>
<body>
<div onmouseout="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
Hover mouse here
<div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
CHILD <br/> It fires if you hover mouse over this child_div
</div>
</div>
<div id="hide_div">TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>
Надеюсь, это поможет:)
Ответ 10
вместо onmouseout используйте onmouseleave.
Вы не показали нам свой конкретный код, поэтому я не могу показать вам на вашем конкретном примере, как это сделать.
Но это очень просто: просто замените onmouseout на onmouseleave.
Вот и все :) Итак, все просто :)
Если вы не уверены, как это сделать, см. объяснение на:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_leave_out
Мир торта :) Наслаждайтесь этим :)
Ответ 11
Есть два способа справиться с этим.
1) Проверьте результат event.target в своем обратном вызове, чтобы узнать, соответствует ли он вашему родительскому div
var g_ParentDiv;
function OnMouseOut(event) {
if (event.target != g_ParentDiv) {
return;
}
// handle mouse event here!
};
window.onload = function() {
g_ParentDiv = document.getElementById("parentdiv");
g_ParentDiv.onmouseout = OnMouseOut;
};
<div id="parentdiv">
<img src="childimage.jpg" id="childimg" />
</div>
2) Или используйте захват события и вызовите event.stopPropagation в функции обратного вызова
var g_ParentDiv;
function OnMouseOut(event) {
event.stopPropagation(); // don't let the event recurse into children
// handle mouse event here!
};
window.onload = function() {
g_ParentDiv = document.getElementById("parentdiv");
g_ParentDiv.addEventListener("mouseout", OnMouseOut, true); // pass true to enable event capturing so parent gets event callback before children
};
<div id="parentdiv">
<img src="childimage.jpg" id="childimg" />
</div>
Ответ 12
Я делаю это как шарм с этим:
function HideLayer(theEvent){
var MyDiv=document.getElementById('MyDiv');
if(MyDiv==(!theEvent?window.event:theEvent.target)){
MyDiv.style.display='none';
}
}
Ah и тег MyDiv
:
<div id="MyDiv" onmouseout="JavaScript: HideLayer(event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
Таким образом, когда onmouseout переходит к дочернему, grand-child и т.д., style.display='none'
не выполняется; но когда onmouseout выходит из MyDiv, он запускается.
Так что не нужно останавливать распространение, использовать таймеры и т.д.
Спасибо за примеры, я мог бы сделать из них этот код.
Надеюсь, это поможет кому-то.
Также можно улучшить так:
function HideLayer(theLayer,theEvent){
if(theLayer==(!theEvent?window.event:theEvent.target)){
theLayer.style.display='none';
}
}
И тогда теги DIVs выглядят следующим образом:
<div onmouseout="JavaScript: HideLayer(this,event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
Итак, более общий, не только для одного div и не нужно добавлять id="..."
на каждый уровень.
Ответ 13
Если у вас есть доступ к элементу, к которому подключено событие внутри метода mouseout
, вы можете использовать contains()
, чтобы увидеть, является ли event.relatedTarget
дочерним элементом или нет.
Поскольку event.relatedTarget
- это элемент, к которому перешла мышь, если он не является дочерним элементом, вы вытеснили элемент.
div.onmouseout = function (event) {
if (!div.contains(event.relatedTarget)) {
// moused out of div
}
}
Ответ 14
Хотя решение вы упомянули использует Jquery, MouseEnter и MouseLeave являются носителями событий DOM, так что вы можете использовать без JQuery.
Ответ 15
На угловых 5, 6 и 7
<div (mouseout)="onMouseOut($event)"
(mouseenter)="onMouseEnter($event)"></div>
Затем на
import {Component,Renderer2} from '@angular/core';
...
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit, OnDestroy {
...
public targetElement: HTMLElement;
constructor(private _renderer: Renderer2) {
}
ngOnInit(): void {
}
ngOnDestroy(): void {
//Maybe reset the targetElement
}
public onMouseEnter(event): void {
this.targetElement = event.target || event.srcElement;
console.log('Mouse Enter', this.targetElement);
}
public onMouseOut(event): void {
const elementRelated = event.toElement || event.relatedTarget;
if (this.targetElement.contains(elementRelated)) {
return;
}
console.log('Mouse Out');
}
}
Ответ 16
Я проверяю исходное смещение элемента, чтобы получить координаты страницы границ элемента, а затем убедитесь, что действие mouseout активируется только тогда, когда мышь выходит за пределы этих границ. Грязный, но он работает.
$(el).live('mouseout', function(event){
while(checkPosition(this, event)){
console.log("mouseovering including children")
}
console.log("moused out of the whole")
})
var checkPosition = function(el, event){
var position = $(el).offset()
var height = $(el).height()
var width = $(el).width()
if (event.pageY > position.top
|| event.pageY < (position.top + height)
|| event.pageX > position.left
|| event.pageX < (position.left + width)){
return true
}
}
Ответ 17
var elem = $('#some-id');
elem.mouseover(function () {
// Some code here
}).mouseout(function (event) {
var e = event.toElement || event.relatedTarget;
if (elem.has(e).length > 0) return;
// Some code here
});
Ответ 18
Если вы добавили (или получили) класс или идентификатор CSS к родительскому элементу, вы можете сделать что-то вроде этого:
<div id="parent">
<div>
</div>
</div>
JavaScript:
document.getElementById("parent").onmouseout = function(e) {
e = e ? e : window.event //For IE
if(e.target.id == "parent") {
//Do your stuff
}
}
Так что материал только запускается, когда событие находится в родительском div.
Ответ 19
Я просто хотел поделиться чем-то с вами.
Мне было трудно с событиями ng-mouseenter
и ng-mouseleave
.
Пример:
Я создал плавающее меню навигации, которое переключается, когда курсор находится над значком.
Это меню было поверх каждой страницы.
- Чтобы обработать отображение/скрытие в меню, я переключаю класс.
ng-class="{down: vm.isHover}"
- Чтобы переключить vm.isHover, я использую события мыши ng.
ng-mouseenter="vm.isHover = true"
ng-mouseleave="vm.isHover = false"
Пока все было хорошо и работало, как ожидалось.
Решение чистое и простое.
Входящая проблема:
В определенном виде у меня есть список элементов.
Я добавил панель действий, когда курсор находится над элементом списка.
Для обработки поведения я использовал тот же код, что и выше.
Проблема:
Я понял, когда мой курсор находится в плавающем меню навигации, а также в верхней части элемента, существует конфликт между собой.
Появилась панель действий, и плавающая навигация была скрыта.
Дело в том, что даже если курсор находится над плавающим меню навигации, запускается элемент списка ng-mouseenter.
Для меня это не имеет смысла, потому что я ожидаю автоматический разрыв событий распространения мыши.
Должен сказать, что я был разочарован, и я потратил некоторое время, чтобы выяснить эту проблему.
Первые мысли:
Я попытался использовать их:
-
$event.stopPropagation()
-
$event.stopImmediatePropagation()
Я объединил много событий ng-указателей (mousemove, mouveover,...), но никто не помогает мне.
Решение CSS:
Я нашел решение с простым свойством css, которое я использую все больше и больше:
pointer-events: none;
В основном, я использую его так (в моих элементах списка):
ng-style="{'pointer-events': vm.isHover ? 'none' : ''}"
С этим сложным событием события ng-mouse больше не будут запускаться, и мое плавающее меню навигации больше не будет закрываться, когда курсор над ним и над элементом из списка.
Далее:
Как и следовало ожидать, это решение работает, но мне оно не нравится.
Мы не контролируем наши события, и это плохо.
Кроме того, вы должны иметь доступ к области vm.isHover
, чтобы достичь этого, и это может быть невозможно или возможно, но грязно в той или иной форме.
Я мог бы сделать скрипку, если кто-то захочет посмотреть.
Тем не менее, у меня нет другого решения...
Это долгая история, и я не могу дать вам картошку, поэтому, пожалуйста, простите меня, мой друг.
Во всяком случае, pointer-events: none
- это жизнь, поэтому запомните ее.