javascript - hljs - textarea code highlighter




Как предотвратить эффект липкого зависания для кнопок на сенсорных устройствах (13)

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

  • Я мог бы добавить класс no-hover ontouchend для каждой кнопки и сделать мой CSS следующим: button:not(.no-hover):hover { background-color: blue; } button:not(.no-hover):hover { background-color: blue; } но это, вероятно, довольно плохо для производительности, и не обрабатывает устройства, такие как Chromebook Pixel (который имеет как сенсорный экран, так и мышь) правильно.

  • Я мог бы добавить класс touch к html:not(.touch) button:hover { background-color: blue; } documentElement и сделать свой CSS следующим: html:not(.touch) button:hover { background-color: blue; } html:not(.touch) button:hover { background-color: blue; } Но это также не работает на устройствах с сенсорным управлением и мышью.

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

Все решения, которые я нашел, кажутся несовершенными. Есть ли идеальное решение?


Я мог бы добавить класс no-hover ontouchend для каждой кнопки и сделать свой CSS как> this: button: not (.no-hover): hover {background-color: blue; }, но это, вероятно, очень плохо для производительности и не обрабатывает> устройства, такие как Chromebook Pixel (который имеет как сенсорный экран, так и мышь)> правильно.

Это правильная отправная точка. Следующий шаг: применить / удалить класс nohover при следующих событиях (демонстрация с jQuery)

buttonelement
 .on("touchend touchcancel",function(){$(this).addClass("nohover")})
 .on("touchstart mouseover",function({$(this).removeClass("nohover")});   

Примечание. Если вы хотите применить другие классы к элементу buttonelement, то: not (.nohover) в CSS больше не будет работать, как ожидалось. Вместо этого вам нужно добавить отдельное определение со значением по умолчанию и! Важным тегом для перезаписывания стиля наведения: .nohover {background-color: white! Important}

Это должно даже правильно обрабатывать устройства, такие как Chromebook Pixel (который имеет как сенсорный экран, так и мышь)! И я не думаю, что это главный убийца производительности ...


Вы можете попробовать это.

JavaScript:

var isEventSupported = function (eventName, elementName) {
    var el = elementName ? document.createElement(elementName) : window;
    eventName = 'on' + eventName;
    var isSupported = (eventName in el);
    if (!isSupported && el.setAttribute) {
        el.setAttribute(eventName, 'return;');
        isSupported = typeof el[eventName] == 'function';
    }
    el = null;
    return isSupported;
};

if (!isEventSupported('touchstart')) {
    $('a').addClass('with-hover');
}

CSS:

a.with-hover:hover {
  color: #fafafa;
}

Вы можете установить фоновый цвет на :active состояние и дать :focus фон defaut.

если вы устанавливаете фоновый цвет через onfocus/ontouch , стиль цвета остается один раз :focus состояние :focus исчезло.
Вам также потребуется сброс onblur чтобы восстановить defaut bg, когда фокус потерян.


Добавьте этот код JS на свою страницу:

document.body.className = 'ontouchstart' in document.documentElement ? '' : 'hover';

теперь в вашем CSS перед каждым наведением добавьте класс зависания следующим образом:

.hover .foo:hover {}

Если устройство касается, класс body будет пустым, в противном случае его класс будет зависать и применяются правила!


Как только CSS Media Queries Level 4 будет реализован, вы сможете это сделать:

@media (hover: hover) {
    button:hover {
        background-color: blue;
    }
}

Или на английском языке: «Если браузер поддерживает правильное / истинное / реальное / неэмулированное зависание (например, имеет основное устройство ввода с мышью), примените этот стиль, когда button s зависнет».

Поскольку эта часть Media Queries Level 4 до сих пор была реализована только в Chrome Chrome, я написал полиполк, чтобы справиться с этим. Используя его, вы можете преобразовать вышеупомянутый футуристический CSS в:

html.my-true-hover button:hover {
    background-color: blue;
}

(Вариант метода .no-touch ). Затем, используя некоторый клиентский JavaScript из того же полинаполнения, который обнаруживает поддержку зависания, вы можете соответственно переключить присутствие класса my-true-hover :

$(document).on('mq4hsChange', function (e) {
    $(document.documentElement).toggleClass('my-true-hover', e.trueHover);
});

Основываясь на ответе Даррена Кукса, который также работает, если вы перенесите свой палец на другой элемент.

См. Раздел « Поиск элемента» во время события touchhend

jQuery(function() {
    FastClick.attach(document.body);
});
// Prevent sticky hover effects for buttons on touch devices
// From https://.com/a/17234319
//
//
// Usage:
// <a href="..." touch-focus-fix>..</a>
//
// Refactored from a directive for better performance and compability
jQuery(document.documentElement).on('touchend', function(event) {
  'use strict';

  function fix(sourceElement) {
    var el = $(sourceElement).closest('[touch-focus-fix]')[0];
    if (!el) {
      return;
    }
    var par = el.parentNode;
    var next = el.nextSibling;
    par.removeChild(el);
    par.insertBefore(el, next);
  }

  fix(event.target);
  var changedTouch = event.originalEvent.changedTouches[0];
  // http://www.w3.org/TR/2011/WD-touch-events-20110505/#the-touchend-event
  if (!changedTouch) {
    return;
  }
  var touchTarget = document.elementFromPoint(changedTouch.clientX, changedTouch.clientY);
  if (touchTarget && touchTarget !== event.target) {
    fix(touchTarget);
  }
});

Демо-версия Codepen


С помощью Modernizr вы можете настраивать свои зависания специально для устройств без касания:

(Примечание: это не работает в snippet-системе , вместо этого проверьте jsfiddle )

/* this one is sticky */
#regular:hover, #regular:active {
  opacity: 0.5;
}

/* this one isn't */
html.no-touch #no-touch:hover, #no-touch:active {
  opacity: 0.5;
}

Обратите внимание :active не должен быть нацелен на .no-touch потому что он работает как на мобильном, так и на рабочем столе.


То, что я делал до сих пор в моих проектах, состояло в том, чтобы вернуть изменения :hover на сенсорные устройства:

.myhoveredclass {
    background-color:green;
}
.myhoveredclass:hover {
    background-color:red;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
    .myhoveredclass:hover, .myhoveredclass:active, .myhoveredclass:focus {
        background-color:green;
    }
}

Все имена классов и именованные цвета только для демонстрационных целей ;-)


Это обычная проблема без идеального решения. Поведение наведения полезно с помощью мыши и в основном вредно с прикосновением. Усугубляет проблему устройства, которые поддерживают сенсорную и мышь (одновременно, не менее!), Такие как Chromebook Pixel и Surface.

Самое чистое решение, которое я нашел, - это включить только поведение наведения, если устройство не считается поддержкой сенсорного ввода.

var isTouch =  !!("ontouchstart" in window) || window.navigator.msMaxTouchPoints > 0;

if( !isTouch ){
    // add class which defines hover behavior
}

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

Я тестировал это на iPhone, iPad, Chromebook Pixel, Surface и различных устройствах Android. Я не могу гарантировать, что он будет работать, когда в микс добавляется общий USB-сенсорный ввод (например, стилус).


Это отлично работает в 2 этапа.

  1. Установите тег тела таким образом, чтобы <body ontouchstart=""> . Я не поклонник этого «взлома», но он позволяет Safari iOS мгновенно реагировать на касания. Не знаете, как это сделать, но оно работает.

  2. Настройте свой осязаемый класс следующим образом:

    // I did this in SASS, but this should work with normal CSS as well
    
    // Touchable class
    .example {
    
        // Default styles
        background: green;
    
        // Default hover styles 
        // (Think of this as Desktop and larger)
        &:hover {
            background: yellow;
        }
    
        // Default active styles
        &:active {
            background: red;
        }
    
        // Setup breakpoint for smaller device widths
        @media only screen and (max-width: 1048px) {
    
            // Important!
            // Reset touchable hover styles
            // You may want to use the same exact styles as the Default styles
            &:hover {
                background: green;
            }
    
            // Important!
            // Touchable active styles
            &:active {
                background: red;
            }
        }
    }
    

Вы также можете удалить любую анимацию в своем сенсорном классе. Android Chrome выглядит немного медленнее, чем iOS.

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


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

Создайте отдельный класс зависания для эффекта зависания. По умолчанию добавьте этот класс hover к нашей кнопке.

Мы не хотим обнаруживать наличие сенсорной поддержки и отключать все эффекты зависания с самого начала. Как упоминалось другими, гибридные устройства набирают популярность; люди могут иметь поддержку касания, но хотят использовать мышь и наоборот. Поэтому удалите только класс hover, когда пользователь на самом деле касается кнопки.

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

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

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

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

var button = document.getElementById('myButton');

button.ontouchstart = function(e) {
  console.log('ontouchstart');
  $('.button').removeClass('button-hover');
  startIntervalToResetHover();
};

button.onclick = function(e) {
  console.log('onclick');
}

var intervalId;

function startIntervalToResetHover() {
  // Clear the previous one, if any.
  if (intervalId) {
    clearInterval(intervalId);
  }
  
  intervalId = setInterval(function() {
    // Stop if the hover class already exists.
    if ($('.button').hasClass('button-hover')) {
      clearInterval(intervalId);
      intervalId = null;
      return;
    }
    
    // Checking of hover state from 
    // http://.com/a/8981521/2669960.
    var isHovered = !!$('.button').filter(function() {
      return $(this).is(":hover");
    }).length;
    
    if (isHovered) {
      console.log('Hover state is active');
    } else {
      console.log('Hover state is inactive');
      $('.button').addClass('button-hover');
      console.log('Added back the button-hover class');
      
      clearInterval(intervalId);
      intervalId = null;
    }
  }, 1000);
}
.button {
  color: green;
  border: none;
}

.button-hover:hover {
  background: yellow;
  border: none;
}

.button:active {
  border: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id='myButton' class='button button-hover'>Hello</button>

Изменить: Другой подход, который я пытался, - вызвать e.preventDefault() в ontouchstart или ontouchend. Кажется, что он останавливает эффект наведения при нажатии кнопки, но также останавливает анимацию нажатия кнопки и предотвращает вызов функции onclick при нажатии кнопки, поэтому вам нужно вызвать их вручную в обработчике ontouchstart или ontouchend. Не очень чистое решение.


Я думаю, что я нашел элегантное (минимальное js) решение для аналогичной проблемы:

Используя jQuery, вы можете вызвать зависание на теле (или любом другом элементе), используя .mouseover()

Поэтому я просто прикрепляю этот обработчик к событию ontouchend элемента так:

var unhover = function() {
  $("body").mousover();  
};
.hoverable {
  width: 100px;
  height: 100px;
  background: teal;
  cursor: pointer;
}

.hoverable:hover {
  background: pink;
}
<div class="hoverable" ontouchend={unhover}></div>

Это, однако, удаляет только: hover pseudoclass из элемента после запуска какого-либо другого события касания, например, салфетки или другого касания


$("#elementwithhover").click(function() { 
  // code that makes element or parent slide or otherwise move out from under mouse. 

  $(this).clone(true).insertAfter($(this));
  $(this).remove();
});






touch