I have created a carousel with a previous and a next button that are always visible. These buttons have a hover state, they turn blue. On touch devices, like iPad, the hover state is sticky, so the button stays blue after tapping it. I don't want that.
I could add a no-hover class ontouchend for each button, and make
my CSS like this: button:not(.no-hover):hover { background-color:
blue; } but that's probably quite bad for performance, and doesn't
handle devices like the Chromebook Pixel (which has both a
touchscreen and a mouse) correctly.
I could add a touch class to the documentElement and make my CSS
like this: html:not(.touch) button:hover { background-color: blue;
} But that also doesn't work right on devices with both touch and a
mouse.
What I would prefer is removing the hover state ontouchend. But it doesn't seem like that is possible. Focusing another element doesn't remove the hover state. Tapping another element manually does, but I can't seem to trigger that in JavaScript.
All the solutions I have found seem imperfect. Is there a perfect solution?
Since this part of CSS Media Queries Level 4 has now been widely implemented since 2018, you can use this:
#media (hover: hover) {
button:hover {
background-color: blue;
}
}
Or in English: "If the browser supports proper/true/real/non-emulated hovering (e.g. has a mouse-like primary input device), then apply this style when buttons are hovered over."
For browsers that do not have this implemented (or didn't at the time of this original answer), I wrote a polyfill to deal with this. Using it, you can transform the above futuristic CSS into:
html.my-true-hover button:hover {
background-color: blue;
}
(A variation on the .no-touch technique) And then using some client-side JavaScript from the same polyfill that detects support for hovering, you can toggle the presence of the my-true-hover class accordingly:
$(document).on('mq4hsChange', function (e) {
$(document.documentElement).toggleClass('my-true-hover', e.trueHover);
});
You can remove the hover state by temporarily removing the link from the DOM. See http://testbug.handcraft.com/ipad.html
In the CSS you have:
:hover {background:red;}
In the JS you have:
function fix()
{
var el = this;
var par = el.parentNode;
var next = el.nextSibling;
par.removeChild(el);
setTimeout(function() {par.insertBefore(el, next);}, 0)
}
And then in your HTML you have:
test
This is a common problem with no perfect solution. Hover behavior is useful with a mouse and mostly detrimental with touch. Compounding the problem are devices which support touch and mouse (simultaneously, no less!) like the Chromebook Pixel and Surface.
The cleanest solution I've found is to only enable hover behavior if the device isn't deemed to support touch input.
var isTouch = !!("ontouchstart" in window) || window.navigator.msMaxTouchPoints > 0;
if( !isTouch ){
// add class which defines hover behavior
}
Granted, you lose hover on devices which may support it. However, sometimes hover impacts more than the link itself, e.g. perhaps you want to show a menu when an element is hovered. This approach allows you to test for the existence of touch and perhaps conditionally attach a different event.
I've tested this on the iPhone, iPad, Chromebook Pixel, Surface, and a variety of Android devices. I can't guarantee that it will work when a generic USB touch input (such as a stylus) is added to the mix.
You can override the hover effect for devices that don't support hover. Like:
.my-thing {
color: #BADA55;
}
.my-thing:hover {
color: hotpink;
}
#media (hover: none) {
.my-thing {
color: #BADA55;
}
}
Tested and verified on iOS 12
Hat tip to https://stackoverflow.com/a/50285058/178959 for pointing this out.
From 2020 You can add hover styles inside media query
#media (hover: hover) and (pointer: fine) {
/* css hover class/style */
}
This media query indicates that styles will work on browsers that not emulate :hover so it will NOT work on touch browsers.
With Modernizr you can target your hovers specifically for no-touch devices:
(Note: this doesn't run on StackOverflow's snippet system, check the jsfiddle instead)
/* 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;
}
Note that :active doesn't need to be targeted with .no-touch because it works as expected on both mobile and desktop.
$("#elementwithhover").click(function() {
// code that makes element or parent slide or otherwise move out from under mouse.
$(this).clone(true).insertAfter($(this));
$(this).remove();
});
From 4 ways to deal with sticky hover on mobile: Here's a way to dynamically add or remove a "can touch" class to the document based on the current input type of the user. It works with hybrid devices as well where the user may be switching between touch and a mouse/trackpad:
<script>
;(function(){
var isTouch = false //var to indicate current input type (is touch versus no touch)
var isTouchTimer
var curRootClass = '' //var indicating current document root class ("can-touch" or "")
function addtouchclass(e){
clearTimeout(isTouchTimer)
isTouch = true
if (curRootClass != 'can-touch'){ //add "can-touch' class if it's not already present
curRootClass = 'can-touch'
document.documentElement.classList.add(curRootClass)
}
isTouchTimer = setTimeout(function(){isTouch = false}, 500) //maintain "istouch" state for 500ms so removetouchclass doesn't get fired immediately following a touch event
}
function removetouchclass(e){
if (!isTouch && curRootClass == 'can-touch'){ //remove 'can-touch' class if not triggered by a touch event and class is present
isTouch = false
curRootClass = ''
document.documentElement.classList.remove('can-touch')
}
}
document.addEventListener('touchstart', addtouchclass, false) //this event only gets called when input type is touch
document.addEventListener('mouseover', removetouchclass, false) //this event gets called when input type is everything from touch to mouse/ trackpad
})();
</script>
I was going to post my own solution, but checking if someone already posted it, I found that #Rodney almost did it. However, he missed it one last crucial that made it uniseful, at least in my case. I mean, I too took the same .fakeHover class addition / removing via mouseenter and mouseleave event detection, but that alone, per se, acts almost exactly like "genuine" :hover. I mean: when you tap on a element in your table, it won't detect that you have "leaved" it - thus keeping the "fakehover" state.
What I did was simply to listen on click, too, so when I "tap" the button, I manually fire a mouseleave.
Si this is my final code:
.fakeHover {
background-color: blue;
}
$(document).on('mouseenter', 'button.myButton',function(){
$(this).addClass('fakeHover');
});
$(document).on('mouseleave', 'button.myButton',function(){
$(this).removeClass('fakeHover');
});
$(document).on('button.myButton, 'click', function(){
$(this).mouseleave();
});
This way you keep your usual hover functionality when using a mouse when simply "hovering" on your buttons. Well, almost all of it: the only drawback somehow is that, after clicking on the button with the mouse, it wont be in hover state. Much like if you clicked and quickly took the pointer out of the button. But in my case I can live with that.
This is what I have come up with so far after studying the rest of the answers. It should be able to support touch-only, mouse-only or hybrid users.
Create a separate hover class for the hover effect. By default, add this hover class to our button.
We do not want to detect the presence of touch support and disable all hover effects from the very beginning. As mentioned by others, hybrid devices are gaining popularity; people may have touch support but want to use a mouse and vice versa. Therefore, only remove the hover class when the user actually touches the button.
The next problem is, what if the user wants to go back to using a mouse after touching the button? To solve that, we need to find an opportune moment to add back the hover class which we have removed.
However, we cannot add it back immediately after removing it, because the hover state is still active. We may not want to destroy and recreate the entire button as well.
So, I thought of using a busy-waiting algorithm (using setInterval) to check the hover state. Once the hover state is deactivated, we can then add back the hover class and stop the busy-waiting, bringing us back to the original state where the user can use either mouse or touch.
I know busy-waiting isn't that great but I'm not sure if there is an appropriate event. I've considered to add it back in the mouseleave event, but it was not very robust. For example, when an alert pops up after the button is touched, the mouse position shifts but the mouseleave event is not triggered.
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://stackoverflow.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>
Edit: Another approach I tried is to call e.preventDefault() within ontouchstart or ontouchend. It appears to stop the hover effect when the button is touched, but it also stops the button click animation and prevents the onclick function from being called when the button is touched, so you have to call those manually in the ontouchstart or ontouchend handler. Not a very clean solution.
It was helpful for me: link
function hoverTouchUnstick() {
// Check if the device supports touch events
if('ontouchstart' in document.documentElement) {
// Loop through each stylesheet
for(var sheetI = document.styleSheets.length - 1; sheetI >= 0; sheetI--) {
var sheet = document.styleSheets[sheetI];
// Verify if cssRules exists in sheet
if(sheet.cssRules) {
// Loop through each rule in sheet
for(var ruleI = sheet.cssRules.length - 1; ruleI >= 0; ruleI--) {
var rule = sheet.cssRules[ruleI];
// Verify rule has selector text
if(rule.selectorText) {
// Replace hover psuedo-class with active psuedo-class
rule.selectorText = rule.selectorText.replace(":hover", ":active");
}
}
}
}
}
}
Add this JS code to your page:
document.body.className = 'ontouchstart' in document.documentElement ? '' : 'hover';
now in your CSS before every hover add the hover class like this:
.hover .foo:hover {}
If the device is touch, the body class will be empty, otherwise its class will be hover and the rules are applied!
I could add a no-hover class ontouchend for each button, and make my CSS like >this: button:not(.no-hover):hover { background-color:
blue; } but that's probably quite bad for performance, and doesn't handle >devices like the Chromebook Pixel (which has both a touchscreen and a mouse) >correctly.
This is the right starting point. Next step:
apply/remove nohover class on following events (demo with jQuery)
buttonelement
.on("touchend touchcancel",function(){$(this).addClass("nohover")})
.on("touchstart mouseover",function({$(this).removeClass("nohover")});
Notice: If You wish to apply other classes to the buttonelement, the :not(.nohover) in the CSS won't work anymore as expected. Than You have to add instead a separate definition with default value and !important tag to overwrite hover style:
.nohover{ background-color: white !important}
This should even handle devices like the Chromebook Pixel (which has both a touchscreen and a mouse) correctly! And I don't think, that this a major performance killer...
A solution that has worked for me:
html {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
Add this code to your stylesheet.
I wanted to get rid of the grey background that appears on iOS Safari when a link is clicked. But it appears to do more. Now clicking a button (with a :hover pseudoclass!) gets opened right away! I only tested it on an iPad, I don't know if it'll work on other devices.
I have nice solution that i would like to share.
First you need to detect if user is on mobile like this:
var touchDevice = /ipad|iphone|android|windows phone|blackberry/i.test(navigator.userAgent.toLowerCase());
Then just add:
if (!touchDevice) {
$(".navbar-ul").addClass("hoverable");
}
And in CSS:
.navbar-ul.hoverable li a:hover {
color: #fff;
}
I went through the similar problem, my application was compatible for all screen sizes had a hell lot of hover effects in the desktop-screen-size / mouse based devices , and later on I realised that touch based devices were causing a condition known as sticky-hover and it was an hurdle for the app to work properly for touch based device users.
We were making use of SCSS in our app. and I defined a mixin to take care of touch based device.
#mixin hover-support {
#media not all and (pointer: coarse) {
&:hover {
#content;
}
}
}
and then I placed all my css classes under the below-mentioned snippet.
#include hover-support() {
// Your css-classes or css that you want to apply on those devices that support hover.
}
For instance we had a class for animating an icon, and which used to trigger once we hover over icon as you can see it from css, but in touch based device it was getting affected by sticky hover-effect and then I placed it inside #include hover-support() in order to ensure that hover only applies to those devices that support hover.
#include hover-support() {
.animate-icon {
-webkit-transition: all 0.2s;
transition: all 0.2s;
&:hover {
transform: scale(1.3);
filter: brightness(85%);
cursor: pointer;
}
}
}
Here's some simple JavaScript code that doesn't require the developer to edit CSS or write any new CSS rules. I wrote this for Bootstrap buttons with class="btn", but it will work with any button that has a specific class name.
The steps are:
determine if this is a touch device
if it is, iterate over every CSS rule in document.styleSheets
delete any rule containing both .btn and :hover
The elimination of all .btn :hover CSS rules ensures that there will be no visual hover effect on a button.
Step 1: Detect Touch Device
Check media query for presence of (hover: none):
const hasMatchMedia = typeof window.matchMedia !== 'undefined';
/**
* determine if device is touch-capable
* true - device is touch-capable
* false - device is not touch-capable
* null - unable to determine touch capability
* #return {null|boolean}
*/
const hasTouch = () => {
if (hasMatchMedia) {
return window.matchMedia('(hover: none)').matches;
}
return null;
};
Step 2: Delete CSS Rules Containing `btn` and `:hover`
/**
* remove all CSS rules contaning both '.btn' and ':hover'
* #return {number} count of rules deleted
*/
function removeBtnHovers () {
let rulesDeleted = 0;
// recursively delete '.btn:hover' rules
function recursiveDelete (rules, styleSheet) {
if (typeof rules === 'undefined' ||
typeof rules.length === 'undefined' ||
rules.length <= 0) {
return;
}
// iterate in reverse order,
// deleting any rule containing both '.btn' and ':hover'
const ruleLen = rules.length;
for (let i = ruleLen - 1; i >= 0; i--) {
const rule = rules[i];
if (typeof rule.cssRules === 'undefined') {
// a standard rule, evaluate it
const cssText = rule.cssText;
if (typeof cssText === 'string' &&
cssText.includes('.btn') &&
cssText.includes(':hover')) {
styleSheet.deleteRule(i);
rulesDeleted++;
}
} else {
// rule contains cssRules, iterate over them
recursiveDelete(rule.cssRules, rule);
}
}
}
// iterate over all style sheets in document
for (const styleSheet of document.styleSheets) {
let rules = styleSheet.cssRules;
if (!rules) { continue; }
recursiveDelete(rules, styleSheet);
}
return rulesDeleted;
}
Complete code is on GitHub and npm.
Live demo at terrymorse.com.
You could set background-color on :active state and give :focus the defaut background.
if you set background-color via onfocus/ontouch, color style remains once :focus state has gone.
You need a reset on onblur as well to restore defaut bg when focus is lost.
This worked for me: put the hover styling in a new class
.fakehover {background: red}
Then add / remove the class as and when required
$(".someclass > li").on("mouseenter", function(e) {
$(this).addClass("fakehover");
});
$(".someclass > li").on("mouseleave", function(e) {
$(this).removeClass("fakehover");
});
Repeat for touchstart and touchend events. Or whatever events you like to get the desired result, for example I wanted the hover effect to be toggled on a touch screen.
I think I've found an elegant (minimum js) solution for a similar problem:
Using jQuery, you can trigger hover on body (or any other element), using .mouseover()
So I simply attach a this handler to the element's ontouchend event like so:
var unhover = function() {
$("body").mousover();
};
.hoverable {
width: 100px;
height: 100px;
background: teal;
cursor: pointer;
}
.hoverable:hover {
background: pink;
}
<div class="hoverable" ontouchend={unhover}></div>
This, however, only removes :hover pseudoclass from the element after some other touch event has been triggered, like swipe or another touch
Based on Darren Cooks answer which also works if you moved your finger over another element.
See Find element finger is on during a touchend event
jQuery(function() {
FastClick.attach(document.body);
});
// Prevent sticky hover effects for buttons on touch devices
// From https://stackoverflow.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 Demo
You can try this way.
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;
}
What I did so far in my projects was was to revert the :hover changes on touch devices:
.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;
}
}
All class names and named colors just for demonstration purposes ;-)
This works perfectly in 2 steps.
Set your body tag to be like this <body ontouchstart="">. I'm not a fan of this "hack", but it allows Safari on iOS to react to touches instantly. Not sure how, but it works.
Set up your touchable class like this:
// 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;
}
}
}
You may want to remove any animation on your touchable class as well. Android Chrome seems to be a little slower than iOS.
This will also result in the active state being applied if the user scrolls the page while touching your class.
Some of the sticky or stuck :hover :focus :active problems on mobile devices may be caused by missing <meta name="viewport" content="width=device-width"> as the browsers try to manipulate the screen.
It can be accomplished by swapping an HTML class. It should be less prone to glitches than removing the whole element, especially with large, image links etc.
We can also decide whether we want hover states to be triggered when scrolling with touch (touchmove) or even add a timeout to delay them.
The only significant change in our code will be using additional HTML class such as <a class='hover'></a> on elements that implement the new behaviour.
HTML
<a class='my-link hover' href='#'>
Test
</a>
CSS
.my-link:active, // :active can be turned off to disable hover state on 'touchmove'
.my-link.hover:hover {
border: 2px dotted grey;
}
JS (with jQuery)
$('.hover').bind('touchstart', function () {
var $el;
$el = $(this);
$el.removeClass('hover');
$el.hover(null, function () {
$el.addClass('hover');
});
});
Example
https://codepen.io/mattrcouk/pen/VweajZv
-
I don’t have any device with both mouse and touch to test it properly, though.
Thats too easy to use javascrpt. thats not hover problem thats focus problem. set outline to none when focus using css.
.button:focus {
outline: none;
}
If you are a CSS-in-JS guy and looking for a solution for this problem, here it is.
You can use JS media queries to implement media queries in CSS-in-JS.
For example, the following snippet adds a hover effect to the button, only if the screen size is greater than 768px.
tag: {
cursor: "pointer",
"&:hover, &:active": window.matchMedia('(min-width: 768px)').matches ? {
transform: "scale(1.3)"
} : null
}
The solution for me was after touchend was to clone and replace the node... i hate doing this but even trying to repaint the element with offsetHeight wasn't working
let cloneNode = originNode.cloneNode( true );
originNode.parentNode.replaceChild( cloneNode, originNode );
Kevin Lee's answer includes a bit at the end that I haven't seen anywhere else, and it helped me solve this for multi-input systems (like a touchscreen laptop) with very little work.
function f() {
console.log("Hi");
}
button:hover {
border: 2px solid blue;
}
<button type="button" onclick="f()" ontouchend="f(); event.preventDefault()">
Say Hi
</button>
Run this on a multi-input device, and you should see that hovering with the mouse pointer creates a blue border, but pressing the button on the touchscreen does not.
The only downside is that any other on-click functionality in your UI framework (ripple animations etc) will likely not fire. You may be able to manually fire them yourself -- in my case it wasn't an issue in the first place.
Related
Having a UI that is meant to be keyboard friendly. It has various controls in the for of buttons etc.
Problem is that when one click a button by keyboard, the button does not get the :active status. I have a strong style to show it gets :focus - but the active status is only triggered when one use mouse and click with main button.
This makes things very user-unfriendly. It looks as if the button is not activated. The page has frozen or the like. One risk users clicking multiple times as they expect a visual feedback.
From what I read at MDN it looks as this is a feature in the CSS3 specification:
Note: On systems with multi-button mice, CSS3 specifies that the :active pseudo-class must only apply to the primary button; on right-handed mice, this is typically the leftmost button.
https://developer.mozilla.org/en-US/docs/Web/CSS/:active
As this is a JavaScript based UI, I have tried to solve this by JS on the click event:
Add, and remove a .activated class.
This does not work as the page is not re-rendered. To fix this without using a setTimeout() I have tried things like these:
https://css-tricks.com/restart-css-animation/#article-header-id-0
element.classList.add('activated');
void element.offsetWidth; // Trigger reflow
element.classList.remove('activated');
No effect.
I have tried to add class, remove element, insert a new with the class, and remove the class. No effect.
Used dataset-active=1 and CSS by that. No effect. Same issue as with adding and removing class.
And some other things.
I have also tried to use setTimeout(), but this is somewhat buggy. Especially if one use Enter key (to trigger multiple clicks.)
In the end I also tried to listen for keydown, keyup etc. setting statuses if it was enter, space or mouse click etc. But that quickly got very messy.
Is there a neat trick for this that I am missing? A solution that is not overly complex.
Sample code
button:focus {
outline: 1px solid blue;
}
button:active {
outline: 4px solid red;
}
<button onclick="counter.value = ++counter.value;">Button</button>
<input id="counter" value="0">
How about that: Adding a data-attribute containing the related keycode to the button. While pressing the key down an active class is assigned and click() and focus() are triggert. When the key is released the active class will be unassigned.
document.onkeydown = function (evt)
{
let btn = document.querySelector ('[data-key=' + CSS.escape (evt.keyCode) + ']');
if (btn)
{
btn.classList.add ('active');
btn.click();
btn.focus();
}
}
document.onkeyup = function (evt)
{
let btn = document.querySelector ('[data-key=' + CSS.escape (evt.keyCode) + ']');
if (btn)
{
btn.classList.remove ('active');
}
}
button:focus {
outline: 1px solid blue;
}
button.active, button:active {
outline: 4px solid red;
}
<button data-key="32" onclick="console.log(1);">Button 1 [space]</button>
<button data-key="16" onclick="console.log(2);">Button 2 [shift]</button>
<button data-key="13" onclick="console.log(3);">Button 3 [enter]</button>
Alternatively you can take a look at accesskey attribute.
I'm making a single-page application with HTML, CSS and Javascript (no jQuery or similar). This application is made of many UI pages that can change via Javascript. The user experience is fine using the mouse on computers, but not so nice with touchscreens (mobile, etc.).
There are many buttons with a CSS hover graphic effect. If I change page tapping one button on a touch screen, the pointer stays there triggering the CSS hover of next elements appearing in the same position when the page is "changed". This effect is very annoying, but I can't figure out how to fix it.
The code is very simple:
CSS
button {
background-color: #XXXXXX;
}
button:hover {
background-color: #ZZZZZZ;
}
HTML
<button onclick="changepage()"></button>
You can use modernizr with Touch Events detection, than use
html.no-touch button:hover {
background-color: #ZZZZZZ;
}
Without modernizr you can add this simple code to append no-touch/touch class to html tag
<script type="text/javascript">
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Windows Phone/i.test(navigator.userAgent)) {
document.getElementsByTagName('html')[0].className += ' touch';
}else{
document.getElementsByTagName('html')[0].className += ' no-touch';
}
</script>
Thank you anyway! Finally I made a very simple script that works perfectly...it is good even for touch computers that have a mouse too (like mine), and of course for mobile phones. There is no need to detect devices! The solution is to add a very small div under the cursor pointer after the page changes, by calling the function refresh_hover(). This div 1px x 1px is removed as soon as the user clicks on it or the cursor goes out from it. In this way the hover effect is removed when the content changes behind the pointer, but then restored when the user does something! You will probably think that is very stupid, but is simple and works very well!
Here it is:
function refresh_hover(){
if(!event){
return false;
}
var x = event.clientX;
var y = event.clientY;
var div = document.getElementById('mouse_div');
if(!div){
document.body.innerHTML=document.body.innerHTML
+'<div style="position: fixed; z-index: 1000; height: 1px; width: 1px; display: block;" id="mouse_div" onmouseout="this.style.display=\'none\'" onclick="this.style.display=\'none\'"></div>';
div = document.getElementById('mouse_div');
}
div.style.display='block';
div.style.top=y+'px';
div.style.left=x+'px'
}
I'm trying to get a links background image to toggle or swap on click for an FAQ accordion expand/contractable div using javascript.
I've gotten things working based on this jsfiddle example (http://jsfiddle.net/QwELf/)
You can see my page working here (http://bit.ly/1hfgcGL)
The problem comes in when you click one of the toggle links a 3rd time. It gets the wrong background image and is then out of sync with how it should look.
Right arrow > for contracted state and downward arrow for expanded state are how they should be but the opposite shows up.
It seems to work just fine in the jsfiddle on the 3rd or more clicks any idea what's going wrong with mine?
Script
<script type="text/javascript">
function changeArrow(element){
var arrow = element;
if(arrow.className == 'background_one'){
arrow.className = 'background_two';
} else {
arrow.className = 'background_one';
}
}
</script>
CSS
.background_one { text-decoration: none; padding-left: 26px; background: url(http://skmgroupwork.com/suntrust/landingpages/307m/chevright.gif) center left no-repeat;}
.background_two { text-decoration: none; padding-left: 26px; background: url(http://skmgroupwork.com/suntrust/landingpages/307m/chevdown.gif) center left no-repeat;}
HTML
<a class="background_one" data-toggle="collapse" data-target="#demo4" onClick="changeArrow(this)">If I transfer my balance to a new Access 3 Equity Line, what will my interest rate be?</a>
You need to check if it has class, not if it is, as you have several on times. As you use jQuery you can use .hasClass(), .addClass() and removeCLass(). You might also want to look at .toggleClass().
function changeArrow(element) {
var $arrow = $(element);
if ($arrow.hasClass('background_one')) {
$arrow.removeClass('background_one');
$arrow.addClass('background_two');
} else {
$arrow.removeClass('background_two');
$arrow.addClass('background_one');
}
}
That is happening because the className also contains the class collapsed the second time it's clicked.
I used IE's debugger and found this:
Perhaps you could use contains instead of equals, like the following (untested, but should work):
function changeArrow(element){
element.className = (arrow.className.contains('background_one') ?
'background_two' :
'background_one');
}
Jsfiddle Demo for working version
Jsfiddle demo for failure version
The situation is complicated. I am trying to create an off-canvas navigation sidebar for mobile devise. The idea is like Facebook mobile and Google Plus mobile.
Now the progress is up to here Jsfiddle Demo for working version. (Note it only shows toggle button when the screen is smaller than 768px. It is designed that way.)
For the next step, I want to allow users exit sidebar mode by clicking the dark shadow area. To do that, I bind & unbind RightHandSide_wrapper.click event with sidebar toggle function.
Jsfiddle demo for failure version
JavaScript:
var toggle_offcanvas = function(){
if(
offcanvas_toggle.prop("checked") == false
){
offcanvas_toggle.prop("checked", true);
rhs_wrapper.css('cursor', 'pointer' );
rhs_wrapper.css('background-color', 'gray' );
rhs_wrapper.bind("click",toggle_offcanvas);
console.log("show slidebar");
}
else
{
offcanvas_toggle.prop('checked', false);
rhs_wrapper.css('cursor', 'default' );
rhs_wrapper.css('background-color', 'inherit' );
rhs_wrapper.unbind("click",toggle_offcanvas);
console.log("hide slidebar");
}
}
;
$(".offcanvas_button").bind("click",toggle_offcanvas);
Here is the problem. After adding rhs_wrapper.bind("click",toggle_offcanvas);, every time I click the toggle button it runs the toggle_offcanvas function twice. This can be observed from console log, every click on toggle button returns two console outputs: show slidebar and hide slidebar.
It looks like the function toggle_offcanvas is recalled while I am binding it.
Please help.
Here's the solution:
http://jsfiddle.net/8XSJJ/
The problem is that you're binding the click handler to the div that contains the button, and this triggers the handler twice. So i added a different layer
<div class="graybg">
</div>
With styling to make it transparent:
.graybg {
position:fixed;
width:100%;
height:100%;
opacity:0.5;
filter:alpha(opacity=50); /* For IE8 and earlier */
background: black;
cursor: pointer;
z-index: 100; /* behind slide_nav but above everything else */
display:none; /* initially hidden */
}
And basically in the code instead of rhs_wrapper you use this new layer.
I have a web application that utilizes a separate print stylesheet to control how the page looks when it comes out of the printer. That was working wonderfully until I recently made some Javascript enhancements to the site. One of these enhancements allows the user to freeze the page header and navigation, as well as table headers. The Javascript behind this does some CSS trickery to freeze the elements on the screen. Unfortunately, applying position: fixed to my header (for example) causes it to print on every page, and this is not a desired effect. How can I use Javascript to tweak element styles on the client-side without affecting the print style?
#media print { #foo { color: blue; } } /* Print definition */
#media screen { #foo { color: green; } } /* Display definition */
document.getElementById('foo').style.color = 'red'; /* Overrides both! */
Instead of changing properties on your elements with this:
document.getElementById('foo').style.color = 'red';
append a new <style> element, for example:
$('<style>#media screen { #foo { color: green; } }</style>').appendTo('head');
It would be better to concatenate all your required changes into one <style> element, if possible.
Add !important to your print rules.
You can try this
#media print { #foo { color: blue !important; } }
The problem is that javascript .style.something, edits the inline css of the element, therefore it will override the normal css class/id rules.
Or you can, work with classes.
document.getElementById('foo').className = 'redText';
and keep the .redText in your regular css file (not the print one), much much better than filling your print css with !important rules.
No good solution! What I ended up doing is utilizing the onbeforeprint and onafterprint functions in IE (I am in the position here that we only have IE users) to "unfreeze" and "refreeze" the elements...
window.onbeforeprint = function() {
document.getElementById('foo').style.position = 'static';
}
window.onload = window.onafterprint = function() {
var el = document.getElementById('foo');
// Get element position and size
// Set left/top/width/height properties
// Set position to fixed
el.style.position = 'fixed';
}
The proper solution is not to poke styles onto nodes, but to instead tie your screen-specific style tweaks to css classes that only affect your screen rendition of things:
#media screen { .freeze { position: fixed; } } /* Display-only definition */
+
document.getElementById('foo').className = "freeze";
As a bonus, this also makes it easy to change tons of styles with just one line of js, which makes things faster and easier to maintain, too.