Как имитировать наведение с сенсорным экраном в браузерах?

120

С таким HTML:

<p>Some Text</p>

Затем немного CSS вроде этого:

p {
  color:black;
}

p:hover {
  color:red;
}

Как я могу позволить долгому касанию на устройстве с сенсорным экраном воспроизводить наведение? Я могу изменить разметку / использовать JS и т. Д., Но не могу придумать простого способа сделать это.

Рич Брэдшоу
источник
Я специально думаю об iPhone OS - это демонстрация CSS-анимации, для которой для многих проще использовать наведение, а не запускать щелчком.
Рич Брэдшоу

Ответы:

172

Хорошо, я разобрался! Это включает в себя небольшое изменение CSS и добавление JS.

Использование jQuery для упрощения:

$(document).ready(function() {
    $('.hover').on('touchstart touchend', function(e) {
        e.preventDefault();
        $(this).toggleClass('hover_effect');
    });
});

На английском языке: когда вы начинаете или заканчиваете прикосновение, включаете hover_effectили выключаете класс .

Затем в вашем HTML добавьте указатель класса на все, с чем вы хотите, чтобы это работало. В своем CSS замените любой экземпляр:

element:hover {
    rule:properties;
}

с участием

element:hover, element.hover_effect {
    rule:properties;
}

И просто для большей полезности добавьте это в свой CSS:

.hover {
-webkit-user-select: none;
-webkit-touch-callout: none;        
}

Чтобы браузер не просил вас скопировать / сохранить / выбрать изображение или что-то еще.

Легко!

Рич Брэдшоу
источник
Это круто. Я разделил функцию domready, потому что toggleвсе испортится, если пользователь коснется одного элемента и перетащит его на другой (например, если они коснулись не того элемента и
попытаются
Я удалил touchendи preventDefault()на своем веб-сайте, и он работает хорошо, но теперь меню не закрываются автоматически при выходе за пределы цели.
Даниил Пронин
Я также хотел бы получить совет, как закрыть такое меню, когда пользователь касается другого места или начинает прокрутку. Я предполагаю, что touchstartна теле может быть другой слушатель (или аналогичный), но я подумал, есть ли лучший способ. Спасибо!
Дэррил Янг,
2
Есть ли способ игнорировать жест прокрутки? Это делает именно то, что мне нужно, но теперь я не могу прокручивать вверх и вниз контент, имеющий такой эффект.
alsobubbly
1
Я удалил preventDefault, так как хочу, чтобы прокрутка была доступна, но спасибо за советы по touchstart и touchend! Дай бог, это единственный ответ, который надежно работает во всех браузерах.
Петреус
54

Все, что вам нужно сделать, это привязать touchstart к родительскому объекту. Примерно так будет работать:

$('body').on('touchstart', function() {});

В функции ничего делать не нужно, оставьте поле пустым. Этого будет достаточно, чтобы навести курсор мыши при касании, поэтому прикосновение будет вести себя больше как: hover, а не как: active. Магия iOS.

Разван Черчелару
источник
2
Позволяет ли это решение выполнить ссылку при втором нажатии?
samuelkobe
1
Нет, он все еще вызывает щелчок при первом прикосновении. Я только что это проверил.
Джек
Отличное решение! Быстро и просто.
Хантер Тернер
41

Попробуй это:

<script>
document.addEventListener("touchstart", function(){}, true);
</script>

И в вашем CSS:

element:hover, element:active {
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-select: none;
-webkit-touch-callout: none /*only to disable context menu on long press*/
}

С этим кодом вам не нужен дополнительный класс .hover!

Ансельм
источник
1
это работает для меня ... некоторые другие вещи, которые могут помочь в этом еще больше, вместо добавления javascript, сделайте следующее: <body ontouchstart = ""> затем добавьте небольшой таймер в событие css: active: tr: active {background-color : # 118aab; переход: все .2s линейные; -webkit-tap-highlight-color: rgba (0,0,0,0); -webkit-user-select: нет; -webkit-touch-callout: none}
Kozy
Работал у меня. Спасибо ... Единственное - немного лагает ... То есть - после нажатия не моментально загружается.
Роман Лосев
Используйте fastclick.js, чтобы убрать задержку
Ансельм,
25

Чтобы ответить на ваш главный вопрос: «Как имитировать наведение при касании в браузерах с сенсорным экраном?»

Просто разрешите «щелкнуть» элемент (нажав на экран), а затем инициируйте hoverсобытие с помощью JavaScript.

var p = document.getElementsByTagName('p')[0];
p.onclick = function() {
 // Trigger the `hover` event on the paragraph
 p.onhover.call(p);
};

Это должно работать, пока hoverна вашем устройстве есть событие (даже если оно обычно не используется).

Обновление: я только что протестировал эту технику на своем iPhone, и, похоже, она работает нормально. Попробуйте здесь: http://jsfiddle.net/mathias/YS7ft/show/light/

Если вместо этого вы хотите использовать «долгое касание» для запуска наведения курсора, вы можете использовать приведенный выше фрагмент кода в качестве отправной точки и получать удовольствие от таймеров и прочего;)

Матиас Байненс
источник
Я не знал об этом бите onhover.call (p), это круто. Хотя нет никакого зависания ...
Рич Брэдшоу
Вы тестировали это с несколькими элементами и долгим касанием? Это означает, что пользователь перемещает палец по экрану и показывает стиль наведения для каждого элемента?
Маркус
Похоже, если вы уже используете jQuery, вы можете просто вызвать событие зависания с самим jQuery, например: jQuery.hover (); Однако не уверен, что API поддерживает это.
Kzqai 02
Упс, это был комментарий, более применимый к посту выше, который на самом деле использует jQuery, извините.
Kzqai 02
Вам нужен специальный скрипт или вызов функции? У меня работает, но только на одной странице, и я не знаю, в чем разница.
Ричард Янг
13

Дальнейшее улучшенное решение

Сначала я использовал подход Рича Брэдшоу , но потом начали появляться проблемы. Выполняя e.preventDefault () в событии touchstart , страница больше не прокручивается, и ни долгое нажатие не может вызвать меню параметров, ни масштабирование двойным щелчком не может завершить выполнение.

Решением может быть определение того, какое событие вызывается, и просто e.preventDefault () в последнем, «touchend» . Так как «touchmove» прокрутки идет перед «touchend», он остается таким же по умолчанию, и «щелчок» также запрещен, поскольку он идет после слов в цепочке событий, применяемой к мобильному устройству, например:

// Binding to the '.static_parent' ensuring dynamic ajaxified content
$('.static_parent').on('touchstart touchend', '.link', function (e) {

    // If event is 'touchend' then...
    if (e.type == 'touchend') {
        // Ensuring we event prevent default in all major browsers
        e.preventDefault ? e.preventDefault() : e.returnValue = false;
    }

    // Add class responsible for :hover effect
    $(this).toggleClass('hover_effect');
});

Но затем, когда появляется меню опций, оно больше не запускает touchchend, отвечающий за выключение класса, и в следующий раз поведение при наведении будет наоборот, полностью перепутанным.

Решением будет, опять же, условное определение того, в каком событии мы находимся, или просто выполнение отдельных событий , и использование addClass () и removeClass () соответственно для touchstart и touchend , гарантируя, что оно всегда начинается и заканчивается соответственно добавление и удаление вместо условного принятия решения о нем. В завершение мы также свяжем обратный вызов удаления с типом события focusout , оставаясь ответственным за очистку любого класса при наведении курсора ссылки, который может оставаться включенным и никогда больше не посещаться, например так:

$('.static_parent').on('touchstart', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('touchend focusout', '.link', function (e) {
    // Think double click zoom still fails here
    e.preventDefault ? e.preventDefault() : e.returnValue = false;
    $(this).removeClass('hover_effect');
});

Внимание: некоторые ошибки все еще возникают в двух предыдущих решениях, и, также думаю (не проверено), масштабирование двойным щелчком по-прежнему не работает.

Аккуратное и, надеюсь, свободное от ошибок (не :)) решение Javascript

Теперь для второго, более чистого, аккуратного и отзывчивого подхода, просто используя javascript (без смешения между классом .hover и псевдо: hover), и откуда вы могли бы напрямую вызывать свое поведение ajax в универсальном (мобильном и настольном) событии click. , Я нашел довольно хорошо ответ на вопрос, из которого я наконец понял, как я могу смешивать события касания и мыши вместе, без того, чтобы несколько обратных вызовов событий неизбежно меняли друг друга в цепочке событий. Вот как:

$('.static_parent').on('touchstart mouseenter', '.link', function (e) {
    $(this).addClass('hover_effect');
});

$('.static_parent').on('mouseleave touchmove click', '.link', function (e) {
    $(this).removeClass('hover_effect');

    // As it's the chain's last event we only prevent it from making HTTP request
    if (e.type == 'click') {
        e.preventDefault ? e.preventDefault() : e.returnValue = false;

        // Ajax behavior here!
    }
});
Александр Росарио
источник
3

Эффект мыши hoverне может быть реализован на сенсорном устройстве. Когда у меня возникла такая же ситуация, safari iosя использовал :activeCSS для достижения эффекта.

то есть.

p:active {
  color:red;
}

В моем случае это работает. Может быть, это также тот случай, который можно использовать без использования javascript. Просто попробуйте.

Сиджо Виджаян
источник
2

Добавьте этот код, а затем установите класс «tapHover» для элементов, с которыми вы хотели бы работать таким образом. При первом нажатии на элемент он получит псевдокласс «: hover» и класс «tappped». Событие щелчка будет предотвращено. Во второй раз вы нажмете тот же элемент - событие щелчка будет запущено.

// Activate only in devices with touch screen
if('ontouchstart' in window)
{
    // this will make touch event add hover pseudoclass
    document.addEventListener('touchstart', function(e) {}, true);

    // modify click event
    document.addEventListener('click', function(e) {
        // get .tapHover element under cursor
        var el = jQuery(e.target).hasClass('tapHover')
            ? jQuery(e.target)
            : jQuery(e.target).closest('.tapHover');

        if(!el.length)
            return;

        // remove tapped class from old ones
        jQuery('.tapHover.tapped').each(function() {
            if(this != el.get(0))
                jQuery(this).removeClass('tapped');
        });

        if(!el.hasClass('tapped'))
        {
            // this is the first tap
            el.addClass('tapped');
            e.preventDefault();
            return false;
        }
        else
        {
            // second tap
            return true;
        }
    }, true);
}
.box {
	float:		left;
	
	display:	inline-block;
	margin:		50px 0 0 50px;
	width:		100px;
	height:		100px;
	overflow:	hidden;
	
	font-size:	20px;
	
	border:		solid 1px black;
}
.box.tapHover {
	background:	yellow;
}
.box.tapped {
	border:		solid 3px red;
}
.box:hover {
	background:	red;
}
<div class="box" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>
<div class="box tapHover" onclick="this.innerHTML = Math.random().toFixed(5)"></div>

l00k
источник
1

Я уверен, что без JS для конкретного устройства (или, скорее, браузера) вам не повезло.

Изменить: думал, что вы хотите избежать этого, пока я не перечитаю ваш вопрос. В случае Mobile Safari вы можете зарегистрироваться, чтобы получать все события касания, аналогично тому, что вы можете делать с помощью собственных UIView-ов. Не могу найти документацию прямо сейчас, но постараюсь.

Йоонас Труссманн
источник
1

Один из способов сделать это - создать эффект наведения при начале касания, а затем удалить эффект наведения при перемещении или завершении касания.

Это то, что Apple должна сказать о работе с сенсорным экраном в целом, когда вы упомянули iPhone.

drawnonward
источник
Содержание ссылки все еще актуально?
Flavien Volken
1

Лично я предпочитаю относить :hoverстили к :focusсостоянию, например:

p {
    color: red;
}

p:hover, p:focus {
    color: blue;
}

Затем со следующим HTML:

<p id="some-p-tag" tabindex="-1">WOOOO</p>

И следующий JavaScript:

$("#some-p-tag").on("touchstart", function(e){
    e.preventDefault();
    var $elem = $(this);

    if($elem.is(":focus")) {
        //this can be registered as a "click" on a mobile device, as it's a double tap
        $elem.blur()
    }
    else {
        $elem.focus();
    }
});
pimbrouwers
источник
1

Решено 2019 - Hover on Touch

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

  1. JQuery добавляет: $ ("p"). On ("touchstart", function (e) {$ (this) .focus (); e.preventDefault ();});

  2. CSS: замените p: hover на p: focus и добавьте p: active

Параметры;

  • заменить селектор jquery p любым классом и т. д.

  • чтобы эффект остался, также удерживайте p: hover и добавьте body {cursor: ponter;} так, чтобы касание в любом месте заканчивало его

  • попробуйте события щелчка и наведения мыши, а также сенсорный запуск в том же коде (но не проверено)

  • удалить e.preventDefault (); чтобы пользователи могли использовать всплывающие окна iOS, например, копировать

Ноты

  • проверено только для текстовых элементов, iOS может обрабатывать вводы и т. д. по-разному

  • Проверено только на iphone XR ios 12.1.12 и ipad 3 ios 9.3.5 с использованием Safari или Chrome.

Даррен
источник
0

Смесь собственного Javascript и jQuery:

var gFireEvent = function (oElem,sEvent) 
{
 try {
 if( typeof sEvent == 'string' && o.isDOM( oElem ))
 {
  var b = !!(document.createEvent),
     evt = b?document.createEvent("HTMLEvents"):document.createEventObject();
  if( b )    
  {  evt.initEvent(sEvent, true, true ); 
    return !oElem.dispatchEvent(evt);
  }
  return oElem.fireEvent('on'+sEvent,evt);
 }
 } catch(e) {}
 return false;
};


// Next you can do is (bIsMob etc you have to determine yourself):

   if( <<< bIsMob || bIsTab || bisTouch >>> )
   {
     $(document).on('mousedown', function(e)
     {
       gFireEvent(e.target,'mouseover' );
     }).on('mouseup', function(e)
     {
       gFireEvent(e.target,'mouseout' );
     });
   }
Codebeat
источник
1
извините за педантизм, но jquery - это javacript
матпол
2
@matpol: jQuery - это javascript, но javascript - это не jQuery. Специально для вас я добавляю слово «чистый», чистый javascript. Доволен сэр?
Codebeat
вы не можете использовать jquery без использования «чистого» javascript. Я только подчеркиваю это потому, что некоторые люди, которые часто посещают SO, похоже, этого не знают.
matpol
5
Смесь встроенного Javascript и jQuery, довольны?
Codebeat
0

Самое простое решение, которое я нашел: у меня было несколько тегов <span> с правилами: hover css. Я переключился на <a href = "javascript: void (0)"> и вуаля. Начали работать стили наведения в iOS.

Бекарио старший
источник
0

Можно также использовать CSS, добавить фокус и актив (для IE7 и ниже) к скрытой ссылке. Пример меню ul внутри div с меню класса:

.menu ul ul {display:none; position:absolute; left:100%; top:0; padding:0;}
.menu ul ul ul {display:none; position:absolute; top:0; left:100%;}
.menu ul ul a, .menu ul ul a:focus, .menu ul ul a:active { width:170px; padding:4px 4%; background:#e77776; color:#fff; margin-left:-15px; }
.menu ul li:hover > ul { display:block; }
.menu ul li ul li {margin:0;}

Это поздно и непроверено, должно работать ;-)

Марк Гроен
источник