Проблема с запуском jQuery mouseleave, когда контейнер имеет поле выбора

У меня есть два контейнера - один вложен внутри другого. Когда я наводил указатель на родителя, я хочу, чтобы появился контейнер для детей. Когда я нажимаю, я хочу, чтобы дочерний контейнер исчезал. Проблема, с которой я столкнулась, - это дочерний контейнер, имеющий форму, которая содержит "поле выбора". Когда пользователь выбирает поле выбора - случайное событие mouseleave случайно запущено.

Как я могу остановить окно выбора при отключении события mouseleave?

Здесь вы можете увидеть мой рабочий код: http://jsfiddle.net/rsturim/9TZyh/3/

Вот сводка моего script:

$('#parent-container').live("mouseenter", function () {
    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function (e) {
    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().hide();              
});

edit: отображается в браузерах на основе WebKit. Ошибка в Firefox и IE7-IE9.

Ответ 1

Так как события mouseleave и mouseenter нестандартны, вы можете получить такие задержки здесь и там. Единственный метод, который я могу предложить исправить, это использование некоторых хаков. Вот http://jsfiddle.net/mPDcu/1/ улучшенная версия вашего кода.

var selectOpened = false;
$('#select-grind-type').click(function(e){
    selectOpened = !selectOpened;
    e.stopPropagation();
});
$('body').click(function(){
    if (selectOpened) {
        selectOpened = false;
    }
})
$('#parent-container').on("mouseenter", function() {
    var $this = $(this),
        $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function(e) {
    if (!selectOpened) {
        var $this = $(this),
            $selectOptionsContainer = $this.find('#child-container');
        $selectOptionsContainer.stop().hide();   
    }
});

Ответ 2

В большинстве случаев вам просто нужно проверить, был ли целевой объект выбранным элементом, и продолжать его только в том случае, если это не так. Кажется намного чище, чем принятое решение, и хорошо работал в моем случае.

Я изменил скрипту: http://jsfiddle.net/Dygerati/uj3ZC/5/

$('#parent-container').live("mouseenter", function() {
    var $this = $(this),
        $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function(e) {
    if(e.target.tagName.toLowerCase() != "select") {
        var $this = $(this),
            $selectOptionsContainer = $this.find('#child-container');
        $selectOptionsContainer.stop().hide();
    }
});

Ответ 3

$('#parent-container').live("mouseenter", function () {
    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function (e) {

    /* Solution */
    if(e.relatedTarget == null) return;
    /************/

    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().hide();              
});

Ответ 4

У меня была та же проблема в проекте, в котором я вношу свой вклад, а другие ответы не сработали для меня. Моим сложным решением было проверить, находится ли позиция мыши внутри объекта события внутри родительского контейнера. Работает очень хорошо!

var layer = $('#parent-container'),
    layerPos = {};
$(layer).mouseenter(function () {
    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0, function(){
        layerPos.x = {
            min: $(layer).offset().left,
            max: $(layer).offset().left + $(layer).width()
        };
        layerPos.y = {
            min: $(layer).offset().top,
            max: $(layer).offset().top + $(layer).height()
        };
    });
})
$(layer).mouseleave(function(e) {
    // check if event mouse position is inside parent container
    var x_con = e.pageX >= layerPos.x.min && e.pageX <= layerPos.x.max;
    var y_con = e.pageY >= layerPos.y.min && e.pageY <= layerPos.y.max;
    if ( x_con && y_con ) {
        return false;
    }

    var $this = $(this),
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().hide();              
});

Вы также можете проверить версию навигатора, чтобы этот код не выполнялся в браузерах, поддерживающих такие функции, как Chrome.

Ответ 5

Это частично решает проблему. Отвяжите событие mouseleave, когда поле выбора получает фокус и снова связывается, когда оно теряет фокус.

http://jsfiddle.net/9TZyh/5/

$('#parent-container').live("mouseenter", function() {
    var $this = $(this);
    $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave",focusOut);
$("#select-grind-type").live("focus",function(){
    $('#parent-container').die("mouseleave");
});
$("#select-grind-type").live("focusout change",function(){
    $('#parent-container').live("mouseleave",focusOut);
});
function focusOut(e) {
    var $this = $(this),
        $selectOptionsContainer = $this.find('#child-container');
    $selectOptionsContainer.stop().hide();
}

Ответ 6

Эти ребята дают вам рабочую альтернативу, но у нее также есть некоторые ошибки. Например, если вы выходите из внешнего поля, когда выпадающее поле все еще открыто, оно не будет исчезать. Я рекомендую вам гораздо более легкую альтернативу, которая также устранит эту ошибку.

Вместо того, чтобы думать в событии mouseleave внутреннего поля, почему бы вам не поразмыслить, чтобы думать наоборот? Я имею в виду, оставляя внутреннюю коробку, а также означает вхождение в другой контейнер. Итак, вы можете сделать outerContainer.mouseenter(function(){ hideInnerBox() });: -)

Очевидно, что для этой цели внутренний ящик не должен быть дочерним элементом outerbox, даже если визуально это выглядит так (для достижения его можно использовать позиционирование css)

Ответ 7

Если вы не возражаете, если в некоторых старых браузерах fade не работает, вы можете быстро сделать это с помощью CSS:

#parent-container {  }
#child-container {
    opacity:0;
    -webkit-transition:opacity 1s ease-in;
       -moz-transition:opacity 1s ease-in;
}
#parent-container:hover #child-container {{
    opacity:1;
    -webkit-transition:opacity 1s ease-out;
       -moz-transition:opacity 1s ease-out;
}

Ответ 8

Итак, я просто столкнулся с аналогичной проблемой с <select>, вложенной в контейнер, и наткнулся на этот вопрос. Вот что я сделал.

               $("#container").mouseover(function(e){
                            var t = $(this);
                            t.addClass('active');
                            var offset = t.offset();
                            var xMin = offset.left;
                            var yMin = offset.top;
                            var xMax = xMin + t.innerWidth();
                            var yMax = yMin + t.innerHeight();

                            t.parent().mousemove(function(e){
                                    if(e.pageX < xMin || e.pageX > xMax-2 || e.pageY < yMin || e.pageY > yMax ){
                                            t.removeClass('active');
                                            // unbind this event
                                            $(this).unbind('mousemove');
                                    }

                            });
                    });

В принципе, когда вы наводите указатель мыши на контейнер, мы собираем его границы и начинаем проверять, находится ли мышь над элементом. Когда мы знаем, что мышь ушла, мы отвязываем слушателя mousemove.

Я бы сделал jsfiddle для вас, но сейчас он работает так медленно!

Надеюсь, что это поможет.

Ответ 9

Вы должны только проверить, является ли текущий элемент потоком вашего контейнера.

Если это прервано обработчик.

Смотрите: потомок jquery

Пример:

ContainerElement.mouseleave(function (e) {
if (ContainerElement.has(e.fromElement).length > 0) return;
// Do your Stuff
});