When you tap on an element in iOS Safari, a "hover state" is activated on the element that was clicked

When using iOS Safari 11–17, I encountered a strange behavior where a hover effect is applied to a link underneath a <div> element that has been removed from the DOM. This happens when the <div> is positioned over the link and is then clicked to disappear.

An animated GIF illustrating this issue can be seen below:

To demonstrate the problem, I added a transparent background to the button so that the link behind it becomes visible. When clicking on the button not directly above the link, the link (labeled as Some link) remains blue instead of changing to its red hover state.

However, if I click on the <div> directly above the link, causing it to be removed, the link does receive the hover effect after the action.

Interestingly, this issue does not occur on Android Chrome, as shown in the example below:

Additionally, I have included the sample HTML/CSS/JS code used in this setup, which is relatively straightforward.

I would prefer it if iOS behaved like Android Chrome in this scenario: clicking on an element that is immediately removed should not trigger a hover state change for an element positioned right behind it.

var button = document.querySelector(".button");
button.addEventListener("click", function() {
  button.parentNode.removeChild(button);
});

var link = document.querySelector('a');
link.addEventListener("click", function() {
  window.alert('clicked!');
});
a:link    { color: blue }
a:visited { color: blue }
a:hover   { color: red }
a:active  { color: green }

.button {
    background: rgba(100, 0, 0, .4);
    position: absolute;
  
    top: 2px;
    left: 2px;
    z-index: 1;

    width: 100px;
    height: 100px;
    line-height: 100px;

    text-align: center;
    border-radius: 5px;
}

.button:hover {
    background: rgba(0, 100, 0, .4);
    cursor: pointer;
}
<a href="#">Some link</a>
<div class="button">Click me!</div>

Answer №1

To disable hover effects, you can use a little hack to achieve it.

Start by adding this line to the "DOMContentLoaded" event handler:

var canHover = !(matchMedia('(hover: none)').matches);
if (canHover) {
  document.body.classList.add('can-hover');
}

This will add the .can-hover class to the body of your HTML document.

Next, update the CSS rule for anchor hover states like this:

Your original CSS rule:

a:hover {
  color: red;
}

Should now be adjusted to:

.can-hover a:hover {
  color: red;
}

By making this change, devices with touch screens will not apply the hover style to any anchor elements.

Answer №2

Did you experiment with employing a media query to check for :hover functionality?

Refer to this link for more information.

Answer №3

Upon close observation, it is evident that this behavior is intentional on iOS devices, even when swiping through a list of items with hover effects.

If your aim is to simply avoid the hover effect, here's a solution you can try. Use a JavaScript function to detect the device, add a specific class to the body element, and eliminate any hover effects as necessary.

Javascript:

function isAppleDevice() {
  return (
    (navigator.userAgent.toLowerCase().indexOf("ipad") > -1) ||
    (navigator.userAgent.toLowerCase().indexOf("iphone") > -1) ||
    (navigator.userAgent.toLowerCase().indexOf("ipod") > -1)
   );
}
if (isAppleDevice()) {
  $('body').addClass('is_apple')
}

CSS:

.is_apple a:hover {
  // apply the same styling as for non-hovered state
}

Answer №4

It appears that Safari behaves this way intentionally. According to Apple's documentation, single-finger taps are treated as mouse events and specific events are triggered during a tap sequence. While there is a 'mouseover' event at the start of the tap, a 'mouseout' event only occurs when clicking on another element. This suggests that Safari assumes the pointer remains in place until the next tap, thus justifying the hover state for the lower element.

I tested your code on Chrome for Android, as well as Chrome and Firefox for Windows (I do not own an Apple device). In all three cases, clicking on the <div> with a mouse over the link resulted in the hover style being applied to the link afterward, unless I moved the cursor away before releasing the mouse. When tapping the <div> with my finger on a touchscreen device, the link did not receive the hover style.

Chrome and Firefox exhibit consistency between touch and mouse interactions, whereas Safari seems to treat touchscreens similarly to how Chrome & Firefox handle mouse clicks, suggesting it views touch input as if it were a mouse input.

I don't believe this behavior requires fixing or circumventing. Unless you can guarantee all users will utilize a specific hardware/browser setup, it's essential to consider the possibility that they may use a mouse, touchscreen, or even both simultaneously. My Windows laptop and Android tablet feature both touchscreens and trackpads, while I occasionally use a USB OTG mouse with my Android phone.

You might want to explore the Pointer Events API, which functions in Chrome, IE, and Edge (distinct from the CSS property mentioned by @erier - pointer-events). As the API handles events rather than CSS, it won't directly aid in achieving correct styling but demonstrates ongoing efforts to ensure browsers respond consistently to diverse input devices.

Answer №5

Give the new media query technique a try to determine if the user's primary input method supports hovering over elements.

@media (hover) {
  // Insert your hover effects here
  a:hover {
    color: red;
  }
}

More information: hover - CSS: Cascading Style Sheets | MDN

Answer №6

It appears that iOS may have made an intentional design choice to mimic the behavior of a mouse, or perhaps Chrome has implemented this design decision themselves. iOS seems to be tailored towards a "mouse-like" experience, as it even displays "mouseovers" on websites despite being primarily touch-based. I've attempted to identify a way to detect if the user is using a mouse in order to disable hover effects, but so far, no suitable solution has been found.

Similarly, Windows seems to have adopted a similar approach with touch functionality, where touching a point effectively moves the mouse pointer to that location and triggers a click action, retaining the presence of the mouse pointer.

If this behavior is proving to be problematic for your design, you might need to consider making adjustments to accommodate it.

Answer №7

I encountered a similar issue that I managed to resolve by temporarily disabling pointer events for a split second just before removing the element, and then re-enabling them after it's been removed.

document.addEventListener("DOMContentLoaded", function ready() {
  var button = document.querySelector(".button");
  button.addEventListener("click", function() {
    document.querySelector('a').classList.add("no-click");
    button.parentNode.removeChild(button);
    setTimeout(function() {
        document.querySelector('a').classList.remove("no-click");
    },1);
  });
  
  document.querySelector('a').addEventListener("click", function() {
    window.alert('clicked!');
  });
});
a:link    { color: blue }
a:visited { color: blue }
a:hover   { color: red }
a:active  { color: green }

.button {
    background: rgba(100, 0, 0, .4);
    position: absolute;
  
    top: 2px;
    left: 2px;
    z-index: 1;

    width: 100px;
    height: 100px;
    line-height: 100px;

    text-align: center;
    border-radius: 5px;
}

.button:hover {
    background: rgba(0, 100, 0, .4);
    cursor: pointer;
}
.no-click {
    pointer-events: none;
}
<a href="#">Some link</a>
<div class="button">Click me!</div>

Answer №8

For those interested in honing their design skills further, delving into the Level 4 Media Query specification could prove invaluable as it allows for targeted styling on devices with hover capabilities.

@media (pointer: fine) {
    display: flex;
}

Answer №9

It seems like this trick could do the job.

To implement it, first disable hover using a specific class. Then, create an event listener that removes the hover-blocking class when the main div is clicked and the mouse moves.

document.addEventListener("DOMContentLoaded", function ready() {
  var button = document.querySelector(".button");
  button.addEventListener("click", function() {
    button.parentNode.removeChild(button);
    var body = document.querySelector("body");
    body.addEventListener("mousemove", function() {
            var someLink = document.querySelector(".some-link");
            someLink.classList.remove("no-hover");
        });
  });
  
  document.querySelector('a').addEventListener("click", function() {
    window.alert('clicked!');
  });
});
a:link    { color: blue }
a:visited { color: blue }
a:hover   { color: red }
a:active  { color: green }

a.no-hover:hover { color: blue }

.button {
    background: rgba(100, 0, 0, .4);
    position: absolute;
  
    top: 2px;
    left: 2px;
    z-index: 1;

    width: 100px;
    height: 100px;
    line-height: 100px;

    text-align: center;
    border-radius: 5px;
}

.button:hover {
    background: rgba(0, 100, 0, .4);
    cursor: pointer;
}
<a href="#" class="some-link no-hover">Some link</a>
<div class="button">Click me!</div>

Answer №10

Consider utilizing pointer-events for improved functionality.

document.addEventListener("DOMContentLoaded", function ready() {
  var button = document.querySelector(".button");
  button.addEventListener("click", function() {
    button.parentNode.removeChild(button);
  });
  
  document.querySelector('a').addEventListener("click", function() {
    window.alert('clicked!');
  });
});
a {pointer-events: none;}
a:link    { color: blue; pointer-events: auto; }
a:visited { color: blue }
a:hover   { color: red; pointer-events: auto; }
a:active  { color: green; pointer-events: auto; }

.button {
    background: rgba(100, 0, 0, .4);
    position: absolute;
  
    top: 2px;
    left: 2px;
    z-index: 1;

    width: 100px;
    height: 100px;
    line-height: 100px;

    text-align: center;
    border-radius: 5px;
}

.button:hover {
    background: rgba(0, 100, 0, .4);
    cursor: pointer;
}
<a href="#">Some link</a>
<div class="button">Click me!</div>

Answer №11

Not sure if this method would be feasible for your actual production code, but it should do the trick for this specific scenario.

One option could be to extract your hover rules into a separate class and then apply this class to the link within the button removal callback function after removing it from the DOM.

If that approach still doesn't achieve the desired outcome, you might consider adding the class within an onmouseover callback. This way, the class would only be applied after the button has been removed and once the link is exited (if applicable) and re-entered.

Although these solutions may seem a bit of a workaround, they can help customize the behavior to adapt to different browsers or operating systems as needed.

Answer №12

There seems to be an issue with browsers where the hover state appears everywhere even when it shouldn't.

To resolve this, I suggest adding a class called .no-touch to the body and styling only elements with that class.

if (!("ontouchstart" in document.documentElement)) {
    document.body.classList.add('no-touch')
}

Here is a consolidated solution:

if (!("ontouchstart" in document.documentElement)) {
    document.body.classList.add('no-touch')
}
document.addEventListener("DOMContentLoaded", function ready() {
  var button = document.querySelector(".button");
  button.addEventListener("click", function(e) {
    button.parentNode.removeChild(button);
  });
  
  document.querySelector('a').addEventListener("click", function() {
    window.alert('clicked!');
  });
});
a:link    { color: blue }
a:visited { color: blue }
.no-touch a:hover   { color: red }
a:active  { color: green }

.button {
    background: rgba(100, 0, 0, .4);
    position: absolute;
  
    top: 2px;
    left: 2px;
    z-index: 1;

    width: 100px;
    height: 100px;
    line-height: 100px;

    text-align: center;
    border-radius: 5px;
}

.button:hover {
    background: rgba(0, 100, 0, .4);
    cursor: pointer;
}
<a href="#">Some link</a>
<div class="button">Click me!</div>

I understand this may not be the exact solution you were seeking, but it will help maintain consistency across Android and iOS platforms and potentially assist others facing the same issue in the future.

Answer №13

Information on MouseEvent relatedTarget can be found in the documentation.

If there is no secondary target for events, the relatedTarget value will be null.

This scenario occurs when the event mouseover or mouseenter is triggered by a click or touch.

someElement.addEventListener(
  "mouseenter",
  (e)=>{
    e.relatedTarget // Will be null if triggered by click or touch, 
                    // otherwise it points to the element where the pointer originated from.
  })

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Deactivate the jQuery function based on the size of the window

I have a jQuery script that should be activated based on the size of the window. The current code works fine when the page loads, but it doesn't respond to window resizing as expected. The jQuery functions don't enable or disable according to th ...

Library for JavaScript coding within a textarea element, complete with correct indentation

Seeking a JavaScript library for coding in a textarea with proper indentation. Came across Behave.js () which lacks the basic feature of indenting a new line based on the last lines indent. It only auto-indents by recognizing braces and parentheses. Codem ...

Retrieve information from json, divide it, and transfer it to the chart for display

Greetings everyone! In my project, I am parsing a JSON file from an online API. However, I have encountered a roadblock while trying to split the data. Despite searching extensively on platforms like YouTube, I haven't been able to find a solution tha ...

How can I personalize the preview feature in a Bootstrap rich text editor?

Currently, I am utilizing Bootstrap's rich text editor by this method $('#editor').wysiwyg();. Utilizing AJAX, I am passing the HTML content created by .wysiwyg to save it in the database. Later on, I plan to retrieve the saved HTML data to ...

When passed as a parameter to a Firebase dynamic link, the access token for Firebase storage is automatically removed

I have uploaded an audio file to firebase storage and I need to include its URL along with an access token as a parameter in the firebase dynamic link. I want my file.component.ts to be publicly shareable on platforms like WhatsApp and Facebook, and below ...

Experiencing issues calling a function in Vue.js while working with the SDK JavaScript?

When attempting to integrate the JavaScript SDK into Vuejs by utilizing a Facebook login button, I encountered an issue: <template> <div> <div class="fb-login-button" data-max-rows="1" data-size="large" data-button-type="login_with" d ...

Can you provide guidance on configuring the image class within a DOM element using echo?

What is the method to set the class attribute of an img element using echo function? <div class="allnis" style="padding: 15px; text-transform: uparcase;"> <?php foreach($tv_id->networks as $prod){ echo "<img val ...

Trouble with feedback form not showing up

I've been working on creating an ajax feedback form, but I'm facing difficulties in getting it to show up properly. The feedback image appears, but clicking on it doesn't trigger any action. Here's my Form: <div id="feedback"> ...

Leveraging the On Keyup Function in Real-Time

How can I modify this code to run after an ajax call using .live method? var element=document.getElementById('txt_url'); element.onkeyup=function(){ var input=element.value; if(input=='') return; if(input.indexOf('h ...

Encountering difficulty executing a function from an external JavaScript file

I'm facing an issue with a basic HTML page that invokes a JavaScript function: <!DOCTYPE html> <html> <script type="text/javascript" src="css/myscript.js"></script> <head> <link rel="stylesheet" href=" ...

Attempting to create a lineup of bricks with varying widths that are directly related to the overall width of the canvas

I'm attempting to create a row of randomly sized bricks on a canvas. The bricks should have varying lengths, but must stay within the confines of the canvas. The canvas has a length of 400, and I want there to be a 10px padding on each side between ...

What is the best way to use jQuery for creating a downloadable JSON file that can be used for saving game data?

Is there a way to create a 'save game file' using Jquery or JavaScript? I know you can use HTML's local files in the browser, but I want users to be able to click a 'save' button and choose a location to download their generated ga ...

Error encountered in React while using an object in useState: TypeError - Unable to access property 'name' of undefined

Encountering an issue with receiving JSON data from a route. When logging the data, it shows up correctly. However, when passing it to setProfileData(data), the updated state becomes undefined. Interestingly, when using setProfileData(data.user.name), it ...

The JQuery duplication process did not function as anticipated

This question builds on a previous one related to jQuery cloning and incrementing IDs. The issue is that when adding new elements, the IDs are not being generated in the correct sequence. For example, if the initial ID is expy-1, clicking "add more" should ...

A malfunction has been detected in EGORefreshTableHeaderView on iOS 5.1

Utilizing the EGORefreshTableHeaderView [1] to retrieve new data from a server and populate a UITableView has been effective for me. However, in iOS 5.1, I have encountered an issue where the EGORefreshTableHeaderView fails to scroll back to its intended ...

Ensure Next.js retains the route when moving from one screen to another

I am currently facing a challenge in Next.js while creating a Dashboard. The root route for this dashboard would be: /dashboard/ Within this route, users can select different stores to access their respective dashboards. When a user clicks on a specific s ...

Arranging array according to the specific importance of a single attribute

Currently, I am leveraging jQuery to analyze a JSON file containing employee details such as their name, department, subdepartment, and additional information. For instance: [ { "LAST":"Betsy", "FIRST":"Jackson", "DEPT":"Customer Service", "SUBDE ...

Tips for creating content surrounding the JSP output: What to include before and after the JSP results

My goal is to enhance the functionality of my JPSs that represent Components. I am seeking a way for these component JSPs to generate HTML before and after their main content is executed. In the current example, we have a file named component.jsp with the ...

Utilizing CSS inheritance in React app to style multiple classes simultaneously

My app features two buttons that serve similar functions on different pages. One button directs users to the search page, while the other takes them back to the home page. The challenge I'm facing is that I need the background image for each button t ...

Accessing and parsing JSON data from directories within directories

My file directory structure is dynamic, with only the name of $(Root Directory) known at runtime. All folders and files are generated dynamically, with all files being in .json format. I am looking to count the number of files and read the content of eac ...