Как обеспечить CSS: зависание применяется к динамически добавленному элементу

У меня есть script, который динамически добавляет полные изображения над эскизами, когда вы наводите на них курсор. Я также дал полные изображения CSS: стиль наведения, чтобы они расширялись до большей ширины (где обычно они ограничены размерами миниатюры). Это работает отлично, если изображение загружается быстро или кэшируется, но если полное изображение занимает много времени, и вы не перемещаете мышь во время загрузки, то, как только он появляется, он обычно остается на ширине эскизов ( non: hover style), пока вы не переместите мышь снова. Я получаю это поведение во всех браузерах, в которых я его пробовал. Мне интересно, если это ошибка, и если есть способ исправить или обойти ее.

Возможно, стоит отметить, что я также попытался сделать то же самое в Javascript с помощью .on('mouseenter') и столкнулся с той же проблемой.

Из-за характера проблемы его трудно воспроизвести, особенно если у вас быстрое соединение. Я выбрал довольно большую фотографию из Википедии, чтобы продемонстрировать, но чтобы она работала, вам, возможно, придется изменить ее на что-то особенно большое или из медленного домена. Также обратите внимание, что вам может понадобиться очистить кеш для последовательных попыток.

Если вы все еще не можете воспроизвести, вы можете добавить искусственную задержку к fullimage.load перед вызовом anchor.show().

HTML:

<img id="image" src="http://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Cairo_International_Stadium.jpg/220px-Cairo_International_Stadium.jpg" />

CSS

.kiyuras-image {
    position: absolute;
    top: 8px;
    left: 8px;
    max-width: 220px;
}

.kiyuras-image:hover {
    max-width: 400px;
}

JS:

$(function () {

    var fullimageurl = 'http://upload.wikimedia.org/wikipedia/commons/3/32/Cairo_International_Stadium.jpg';

    var fullimage = $('<img/>')
        .addClass('kiyuras-image')
        .load(function () {
            anchor.show();
        });

    var anchor = $('<a/>').hide().append(fullimage);

    $('body').prepend(anchor);

    $("#image").on('mouseenter', function () {
        fullimage.attr('src',fullimageurl);
        $(this).off('mouseenter');
    });

});

JS Bin

Обновлен JS Bin с добавленной задержкой на 1,5 секунды (надеюсь, сделает проблему более четкой)

Снова: Воспроизведение проблемы включает очистку кеша большого изображения, а затем зависание над исходным изображением для начальной загрузки большого изображения, а затем не перемещение мыши во время загрузки. Предполагаемое поведение заключается в том, что большое изображение должно правильно использовать псевдо-класс: hover, когда он в конечном итоге загружается. Проблема, которую я вижу, когда требуется загрузка более ~ 0,75 с для загрузки, заключается в том, что она не берет на себя: наведите курсор, пока вы немного не пошевелите мышью.

Изменить. См. мои комментарии по поводу ответа @LucaFagioli для получения дополнительной информации о моем случае использования.

Изменить, сиквел. Я думал, что уже сделал это, но я просто попытался воспроизвести проблему в Firefox, и я не мог. Возможно, это ошибка Chrome?

Ответ 1

Большинство браузеров обновляют свои состояния hover только тогда, когда курсор перемещается по элементу хотя бы на один пиксель. Когда курсор вводит эскиз img, он получает hover и запускает ваш обработчик mouseenter. Если вы держите курсор до тех пор, пока не загрузится полноразмерное изображение, ваш старый img (миниатюра) сохранит состояние hover, а новый не получит его.

Чтобы заставить его работать в этих браузерах, переместите псевдо-класс hover в общий родительский элемент в CSS; например, заключить оба img в span.

Ответ 2

Если селекторы верны, CSS будет применяться ко всем элементам, динамическим или иным образом. Это включает в себя все псевдо классы и будет изменяться как атрибуты в изменении DOM.

Ответ 3

[Редактировать: хотя мое объяснение может представлять интерес, решение pozs выше лучше, поэтому я предлагаю использовать это, если вы можете.]

Спецификация псевдокласса hover довольно расслабленная относительно того, когда она должна быть активирована:

CSS не определяет, какие элементы могут находиться в вышеуказанных состояниях, или как государства вводятся и уходят. Сценарии могут меняться реагируют ли элементы на пользовательские события или нет, и разные устройства и УА могут иметь разные способы указания или активирующие элементы.

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

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

Демо: JS Bin (на основе вашего отложенного примера).

JavaScript:

$("#image")
  .on('mouseenter', function () {
    fullimage.attr('src',fullimageurl).toggleClass('mouseover', true);
    $(this).off('mouseenter');
  })
  .mouseleave(function() {
    fullimage.toggleClass('mouseover', false);
  });

CSS

.kiyuras-image:hover, .kiyuras-image.mouseover {
    max-width: 400px;
}

Ответ 4

TL; DR: вы не можете полагаться на :hover на динамически добавленные элементы под курсором. Однако есть обходные пути, доступные как в чистом CSS, так и в Javascript.

Я поддерживаю ответы Иордана Грея и Поца, и я бы хотел наградить их и щедростью. Джордан Грей обратился к проблеме re: спецификация CSS несколько убедительно и предложила другое исправление, которое все еще допускало: зависание и другие эффекты CSS, такие как переходы, за исключением нагрузки. posz обеспечил решение, которое работает еще лучше и позволяет избежать Javascript для любого из событий наведения; Здесь я предоставляю по существу одно и то же решение, но вместо div используется div. Я решил присудить его ему, но я думаю, что вклад Иордании был существенным. Я добавляю и принимаю свой собственный ответ, потому что я почувствовал необходимость подробно рассказать обо всем этом. (Изменить: Изменено, я принял posz ')

Иордания указала спецификацию CSS2; Вместо этого я обращусь к CSS3. Насколько я могу судить, они не отличаются по этому вопросу.

Под псевдоклассом понимается: hover, который относится к элементам, которые пользователь "назначил с помощью указывающего устройства". Точное определение поведения преднамеренно оставлено неопределенным, чтобы допускать различные виды взаимодействия и средства массовой информации, что, к сожалению, означает, что спецификация не затрагивает такие вопросы, как: "Если новый элемент, который появляется под указывающим устройством, применяет этот псевдокласс?" Это трудный вопрос. Какой ответ будет согласован с намерением пользователя в большинстве случаев? Динамическое изменение страницы, с которой пользователь взаимодействует, обычно является результатом постоянного взаимодействия пользователя или подготовки к тому же. Поэтому я бы сказал, да, и большинство современных браузеров, похоже, согласны. Обычно, когда вы добавляете элемент под курсором,: hover немедленно применяется. Вы можете увидеть это здесь: jsbin, который я изначально опубликовал. Обратите внимание, что если есть время для загрузки большего изображения, возможно, вам придется обновить страницу чтобы заставить его работать, по причинам, по которым я пойду.

Теперь есть аналогичный случай, когда пользователь активирует сам браузер с курсором, неподвижным над элементом с правилом: hover; должен ли он применяться в этом случае? В этом случае мышь "зависает" не является результатом прямого взаимодействия с пользователем. Но указывающее устройство обозначает его, верно? Кроме того, любое движение мыши, безусловно, приведет к однозначному взаимодействию. Ответ на этот вопрос сложнее, и браузеры отвечают на него по-разному. Когда вы активируете их, Chrome и Firefox не меняются: наведите указатель мыши до тех пор, пока вы не переместите мышь (даже если вы активировали их одним нажатием!). Internet Explorer, с другой стороны, обновляет: состояние зависания, как только оно активируется. Фактически, он обновляет его, даже если он неактивен, пока это первое видимое окно под мышью. Вы можете увидеть это самостоятельно, используя jsbin, связанный выше.

Вернемся к первому случаю, хотя, потому что это происходит, когда возникает моя текущая проблема. В моем случае пользователь не перемещал мышь в течение значительного периода времени (более секунды), и элемент добавляется непосредственно под курсором. С этим легче было бы утверждать, что это случай, когда взаимодействие с пользователем неоднозначно и где псевдокласс не должен переключаться. Лично я считаю, что он все равно должен применяться. Однако большинство браузеров, похоже, не согласны со мной. Когда вы наводите курсор на изображение в первый раз, а затем не перемещаете мышь в этот jsbin (который я опубликовал в своем вопросе продемонстрируйте проблему, и, как и первая, имеет простой: селектор hover): класс hover не применяется в текущих браузерах Chrome, Opera и IE. (Safari также не применяет его, но это интересно, если вы продолжаете нажимать клавишу на клавиатуре.) Однако в Firefox применяется класс: hover . Поскольку Chrome и Firefox были единственными, с которыми я сначала тестировался, я подумал, что это ошибка в Chrome. Тем не менее, спецификация более или менее полностью умалчивает об этом. Большинство реализаций утверждают, что нет; Firefox и я говорю aye.

Вот соответствующие разделы спецификация:

Псевдокласс класса hover применяется, когда пользователь указывает элемент с указывающим устройством, но не обязательно его активирует. Например, визуальный пользовательский агент может применить этот псевдокласс, когда курсор (указатель мыши) нависает над полем, сгенерированным элементом. Пользовательским агентам, которые не поддерживают интерактивные медиа, не нужно поддерживать этот псевдокласс. Некоторые совместимые пользовательские агенты, которые поддерживают интерактивные носители, могут не поддерживать этот псевдокласс (например, перо-устройство, которое не обнаруживает зависание).

[...]

Селектор не определяет, является ли родительский элемент элемента:: active или:: hover также находится в этом состоянии.

[...]

Примечание. Если состояние ": hover" применяется к элементу, потому что его дочерний элемент указан указательным устройством, тогда возможно: "наклон" применять к элементу, который находится не под указательным устройством.

Итак! На обходные пути! Как несколько ревностно отметили в этой теме, Javascript и jQuery также предлагают решения для этого, полагаясь на события DOM "mouseover" и "mouseenter". Я сам изучил немало таких решений, как до, так и после задавания этого вопроса. Однако у них есть свои проблемы, они имеют немного другое поведение, и они обычно включают просто переключение класса CSS в любом случае. Кроме того, зачем использовать Javascript, если это не нужно?

Мне было интересно найти решение, которое использовало: hover и ничего больше, и это он (jsbin). Вместо того, чтобы помещать: наведите указатель на добавляемый элемент, вместо этого мы помещаем его в существующий элемент, содержащий этот новый элемент и занимающий одно и то же физическое пространство; в этом случае div, содержащий как миниатюру, так и новое увеличенное изображение (которое, если оно не зависает, будет иметь тот же размер, что и div и миниатюра). Это, по-видимому, довольно специфично для моего варианта использования, но, вероятно, это может быть выполнено в целом с использованием позиционированного div с тем же размером, что и новый элемент.

Добавление: после того как я закончил составление этого ответа, pozs предоставил в основном то же самое решение, что и выше!

Компромисс между этим и одним из решений полного Javascript состоит в том, чтобы иметь одноразовый класс, который будет эффективно полагаться на события Jover на Javascript/DOM при добавлении нового элемента, а затем удалять все это и полагаться на: надвигаться вперед. Это решение Джордан Грей предложил (Jsbin)

Обе эти функции работают во всех браузерах, которые я пробовал: Chrome, Firefox, Opera, Safari и Internet Explorer.

Ответ 5

Из этой части вашего вопроса: "Это отлично работает, если изображение загружается быстро или кэшируется, но если полное изображение занимает много времени, и вы не перемещаете мышь во время загрузки",

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

Ответ 6

Вы можете сделать что-то вроде этого: http://jsfiddle.net/jR5Ba/5/

В заключение добавьте макет загрузки перед вашим изображением, а затем добавьте div, содержащий ваше большое изображение, с обратным вызовом .load(), чтобы удалить загружаемый слой.

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

$imageContainer = $("#image-container");    
$image = $('#image');

$imageContainer.on({
    mouseenter: function (event) {    
       //Add a loading class
       $imageContainer.addClass('loading');
       $image.css('opacity',0.5); 

       //Insert div (for styling) containing large image            
       $(this).append('<div><img class="hidden large-image-container" id="'+this.id+'-large" src="'+fullimageurl+'" /></div>');

       //Append large image load callback            
       $('#'+this.id+'-large').load(function() {
           $imageContainer.removeClass('loading');
           $image.css('opacity',1);
           $(this).slideDown('slow');
           //alert ("The image has loaded!");        
       });
    },            
    mouseleave: function (event) {
       //Remove loading class
       $imageContainer.removeClass('loading');
       //Remove div with large image 
       $('#'+this.id+'-large').remove();
       $image.css('opacity',1);             
    }        
});

ИЗМЕНИТЬ

Ниже приведена новая версия скрипта, включающая слой загрузки нужного размера с анимацией при отображении большого изображения: http://jsfiddle.net/jR5Ba/6/

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

Ответ 7

Не позволяйте тегу IMG добавляться в DOM, пока у него не будет загружаемого изображения. Таким образом, событие Load не будет срабатывать, пока изображение не будет загружено. Вот измененный JS:

$(function () {

    var fullimageurl = 'http://upload.wikimedia.org/wikipedia/commons/3/32/Cairo_International_Stadium.jpg';

    var fullimage = $('<img/>')
        .addClass('kiyuras-image')
        .load(function () {
            anchor.show(); // Only happens after IMG src has loaded
        });

    var anchor = $('<a/>').hide();

    $('body').prepend(anchor);

    $("#image").on('mouseenter', function () {
        fullimage.attr('src',fullimageurl); // IMG has source
        $(this).off('mouseenter');
        anchor.append(fullimage); // Append IMG to DOM now.
    });

});

Ответ 8

Я не уверен на 100%, почему объявление :hover запускается только при небольшом движении мыши. Возможная причина может заключаться в том, что технически вы можете не нависнуть над элементом. В основном вы перемещаете элемент под курсором во время его загрузки (пока полное изображение не будет полностью загружено, элемент A имеет display: none и поэтому не может быть в состоянии :hover). В то же время это не объясняет разницу с меньшими изображениями, хотя...

Итак, обходным путем является просто использовать JavaScript и оставить выражение :hover из уравнения. Просто покажите пользователю два разных элемента IMG в зависимости от состояния зависания (переключается в JavaScript). В качестве дополнительного преимущества изображение не нужно масштабировать вверх и вниз динамически браузером (визуальный сбой в Chrome).

См. http://jsbin.com/ifitep/34/

UPDATE. Используя JavaScript, чтобы добавить класс .active на большом изображении, вполне возможно использовать собственные анимации CSS. См. http://jsbin.com/ifitep/48

Ответ 9

Я сделал это, и он работал на Chrome (версия 22.0.1229.94 м): Я изменил css следующим образом:

.kiyuras-image{
    position: absolute;
    top: 8px;
    left: 8px;
    max-width: 400px;
}
.not-hovered{
    max-width: 220px;
}

и script следующим образом:

$(function(){
    var fullimageurl = 'http://upload.wikimedia.org/wikipedia/commons/3/32/Cairo_International_Stadium.jpg';

    var fullimage = $('<img/>')
        .addClass('kiyuras-image')
        .load(function () {
            anchor.show();
        });

    var anchor = $('<a/>').hide().append(fullimage);

    $('body').prepend(anchor);

    $('.kiyuras-image').on('mouseout',function(){
        $(this).addClass('not-hovered');
    });
    $('.kiyuras-image').on('mouseover',function(){
        $(this).removeClass('not-hovered');
    });

    $("#image").one('mouseover', function(){
        fullimage.attr('src',fullimageurl);
    });
});

В основном я считаю, что это ошибка Chrome при обнаружении/рендеринге статуса "зависания"; на самом деле, когда я попытался просто изменить css как:

.kiyuras-image{
    position: absolute;
    top: 8px;
    left: 8px;
    max-width: 400px;
}
.kiyuras-image:not(:hover) {
    position: absolute;
    top: 8px;
    left: 8px;
    max-width: 220px;
}

он по-прежнему не работал.

PS: извините за мой английский.