:hover
. For starters, iOS7, for example, will not respond to hover unless the element, or its parent, has clicked event handling. This is clearly seen here in this example: jsfiddle.net/H8EmG - you don’t see any underscores with your finger on the text. In this example, jsfiddle.net/H8EmG/1 “poking” with your finger in the text will lead to its underlining. An interesting fact - until we push another element, the text will sit under the hover ...alert('click')
. This is because iOS understands that for :hover
something is hidden, and tries not to break the behavior laid down by the author of the site.div
that is hidden by default and is shown when you hover over the line. Implemented through :hover
overflow: scroll
enabled overflow: scroll
click
(and alert
) you will not see how not to try to make a line.-webkit-overflow-scrolling: touch
on the container of the label that really helped with the example from jsFiddle, but did not help with the real application.:hover
replaced with a CSS class that “throws” in code, catching mouseenter
/ mouseleave
? This simple fix actually solves everything. Even begins to work "more fun" - no need to click twice. From the first click, we get both alert and checkbox: jsfiddle.net/822eG/10MutationObserver
(which is in iOS 6-7), we monitor the insertion of link
tags into the head
document — we can afford it, because All styles we obviously connect using require.js and in Safari this is guaranteed to be a new link
link
let's go over document.styleSheets
and analyze them ...:hover
display
other than none
and visibility
other than visible
:hover
with. .hover
(i.e., pseudo-class with a regular class) ...mouseenter
/ mouseleave
for the found selector, more precisely for the part that is located before :hover
styleSheet
contains a collection of rules
, in which the rules themselves lie. Each rule has the property selectorText
which can be changed on the go. And also has a collection of style
where firstly contains a set of properties specified in this style - they are stored as an "array". The style
has .length
, going through the length we get all the properties that are changed in this style. Secondly, the style
contains the values ​​of the changed properties. At the index equal to the name of the property the value of the property is stored. .myClass:hover .block, .myItem:hover .element { color: red; display: block; }
selectorText == '.myClass:hover .block, .myItem:hover .element'
, style.length == 2
, style[0] == 'color'
, style[1] == 'display'
, style.color == 'red'
and style.display == 'block'
.rules
takes the lion's share of time. Perhaps WebKit initializes this property lazily and the first call initiates some deep parsing of styles into a set of objects. $(document).ready(function(){ // , , if (!$ws._const.browser.isMobileSafari) { return; } var $body = $('body'); // function addPseudoHover() { this.classList.add('ws-pseudo-hover'); } // "" function removePseudoHover() { this.classList.remove('ws-pseudo-hover'); } // [].filter(...) function uniq(item, index, array) { return array.indexOf(item, index + 1) == -1; } function trimHoverBase(selector) { return selector.substr(0, selector.indexOf(':hover')).trim(); } function filterHoverSelectors(selector) { return selector.indexOf(':hover') != -1; } function createBodyDelegate(hoverSelector){ $body.delegate(hoverSelector, 'mouseenter', addPseudoHover); $body.delegate(hoverSelector, 'mouseleave', removePseudoHover); } function processMutationRecord(mutationRecord) { var needRefresh = false; if (mutationRecord.addedNodes) { for(var i = 0, l = mutationRecord.addedNodes.length; i < l; i++) { if (mutationRecord.addedNodes[i].nodeName == 'LINK') { needRefresh = true; break; } } } if (needRefresh) { checkStylesheetSetDebonuced(); // } } function checkStylesheetSet() { var allHoverSelectors = [], allRules = [], sheet, sheetCheckResult; for(var i = 0, l = document.styleSheets.length; i < l; i++) { sheet = document.styleSheets[i]; // , if (sheet.processed || sheet.rules.length === 0) { continue; } sheetCheckResult = checkCSSRuleSet(sheet); if (sheetCheckResult.rules.length > 0 && sheetCheckResult.selectors.length > 0) { Array.prototype.push.apply(allHoverSelectors, sheetCheckResult.selectors); Array.prototype.push.apply(allRules, sheetCheckResult.rules); } // sheet.processed = true; } // allRules.forEach(function(aRule){ aRule.selectorText = aRule.selectorText.replace(':hover', '.ws-pseudo-hover'); }); // , body allHoverSelectors.map(trimHoverBase).filter(uniq).forEach(createBodyDelegate); } var checkStylesheetSetDebonuced = checkStylesheetSet.debounce(420); function checkCSSRuleSet(sheet) { var result = { selectors: [], rules: [] }; for(var i = 0, l = sheet.rules.length; i < l; i++) { var rule = sheet.rules[i]; if (rule.styleSheet && rule.href /* instanceof CSSImportRule*/) { // @import checkCSSRuleSet(rule.styleSheet); } else if (rule.selectorText /* instanceof CSSStyleRule*/) { var hoverSelectors = getHoverSelectors(rule); if (hoverSelectors.length > 0) { if (checkStyles(rule)) { Array.prototype.push.apply(result.selectors, hoverSelectors); result.rules.push(rule); } } } } return result; } function checkStyles(rule) { for(var i = 0, l = rule.style.length; i < l; i++) { var styleItem = rule.style[i]; if (styleItem == 'display' && rule.style.display !== 'none') { return true; } if (styleItem == 'visibility' && rule.style.visibility !== 'hidden') { return true; } } return false; } function getHoverSelectors(rule) { return rule.selectorText.split(',').filter(filterHoverSelectors); } // head new MutationObserver(function(mutationRecords){ mutationRecords.forEach(processMutationRecord); }).observe(document.getElementsByTagName('head')[0], { childList: true }); });
Source: https://habr.com/ru/post/212959/
All Articles