最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why is my a:link color not showing properly before a keyframe animation on the click that changes it to a:visited?

programmeradmin0浏览0评论

I'm new to CSS and have been playing around with keyframe animations. I currently am using both CSS and JS in order to achieve the following:

  • apply a keyframe animation when a link is hovered over

  • apply a second animation when a link is no longer hovered over, but hasn't been clicked

  • apply a third animation when a link is clicked (and js to delay the nav for a moment so the animation can play, but only the first time it's clicked - after that it returns to normal link behavior)

My problem is that all my links are the a:visited color, even before they're clicked. I'm sure this has to do with how I set up the keyframe animations, but I can't figure out why - the hover-in and hover-out animations begin with the unvisited link color (hover in) and end with currentColor (hover out). If I change currentColor to the unvisited link color, the links ignore the a:visited color.

I don't think the on-click animation is at fault, since it only applies after a:active has been used (I also tried a:focus but it didn't seem to make a difference), but it does end on the visited link color.

(There are other issues, like the hover out animation playing on page load despite being paused, and the browser not remembering the visited state between page loads, but I can live with those if the link color behaves.)

Alternately, could it be the js doing this? I don't understand js very well at all, I had a lot of help with it.

ETA: I added animation-fill-mode: none; to the keyframe animations, but it didn't make a difference.

ETA2: fixed link color in css, but keyframe endstate for hover-out still defaults to black

ETA3: Fixed the default color, but there doesn't seem to be a pure-CSS solution to fade the hover-out from black to the current link color - it always defaults to either black, or the reddish color, rather than whatever condition the current link has, a:visited (black) or a:link (reddish).

document.querySelectorAll('a:not(.sitenav a):not(.footer a)').forEach(function (link) {
  let isAnimating = false;  // Flag to track if animation is already happening
  const linkId = link.href; // Use the link's href as a unique identifier
  let hasAnimatedOnce = localStorage.getItem(linkId) === 'true'; // Retrieve the value from localStorage

  link.addEventListener('click', function (e) {
    // If the link has been clicked before (stored in localStorage), allow normal behavior
    if (hasAnimatedOnce) {
      return; // Normal navigation will happen
    }

    // If animation is already happening, ignore the click
    if (isAnimating) {
      return;  // Do nothing if animation is in progress
    }

    // Prevent default navigation behavior to wait for the animation
    e.preventDefault();

    // Mark that the animation is starting
    isAnimating = true;

    // Reset the animation by removing it first (so it can be reapplied)
    this.style.animation = '';  // Remove the animation to allow it to play again

    // Trigger the animation by adding the animation property again
    this.style.animation = 'active-animation 3s forwards';  // Adjust timing as necessary

    // Store the linkElement to use later in the animationend handler
    this._linkElement = this;
  });

  // Separate animationend listener outside of click event listener
  link.addEventListener('animationend', function () {
    // If the link element was stored and animation is complete
    if (this._linkElement) {
      // Now navigate to the link's href after the animation finishes
      window.location.href = this._linkElement.href;

      // Reset the animation flag so the link can be clicked again
      isAnimating = false;

      // Mark that the link has been clicked and animated once
      hasAnimatedOnce = true;
      
      // Save the state to localStorage so the flag persists across sessions
      localStorage.setItem(linkId, 'true');
      
      // Clear the stored link element reference
      delete this._linkElement;
    }
  });
});
/* Base state for the link - initial color is reddish */
 
 
   a:link {
       position: relative;  /* Ensure the link is positioned */
        z-index: 1; /* Ensure the link is above other elements */
      color: #660e0e;  /* Start as reddish */
      pointer-events: auto;
      text-decoration: none;
      transition: color 0.4s ease;
    }
    
    
 a:not(.sitenav a):not(.footer a) {
  animation-play-state: paused; /* Prevent any animation from starting initially */
  pointer-events: auto;
  position: relative; /* Ensure the link is positioned */
  text-decoration: none;
}
 
 



/* Ensuring the link remains clickable during the animation */
    a:hover {
      pointer-events: auto; /* Ensures pointer events remain enabled */
    }



    /* Hover-in animation for normal hover effect (only when not clicked) */
    a:not(.sitenav a):not(.footer a):not(:active):hover {
      animation: hover-in 3s forwards; /* Add hover animation */
    }

 

/* Hover-out animation when the mouse leaves, but only if the link hasn't been clicked */
a:not(.sitenav a):not(.footer a):not(:active):not(:hover) {
  animation: hover-out 4s none cubic-bezier(.45,.05,.55,.95);
}


    /* visited link */
a:visited {
  color: black;
}



    
    /* selected link */
a:not(.sitenav a):not(.footer a):active {
  animation-name: active-animation;
  animation-duration: 3s;
  animation-fill-mode: none;
} 


 

    /* Active link animation (bright red -> black) */
    @keyframes active-animation {
      0% {
        color: #b80909; /* Start as bright red */
        text-shadow: 0 0 20px darkred, text-shadow: 0 0 5px brown;
      }
      10% {
        font-size: 1.02em; /* embiggen */
        color: #b80909;
        text-shadow: 0 0 20px darkred, text-shadow: 0 0 10px brown;
      }
      20% {
          color: black; /* Fade back to black */
      }
      30% {
        font-size: 1.04em;
      }
      60% {
        text-shadow: 0 0 10px darkred, 0 0 5px brown;
        font-size: ''; /* shrink again */
      }
      100% {
        text-shadow: 0 0 0px darkred, text-shadow: 0 0 0px brown;
        color: '';
      }
    }


    /* Hover-in animation (reddish to black with shadow) */
    @keyframes hover-in {
      0% {
        color: ''; /* Start as link or visited */
        text-shadow: none;
      }
      100% {
        color: black;
        text-shadow: 0 0 30px #850000, 0 0 10px #110402, 0 0 5px #110402;
      }
    }

    /* Hover-out animation (black to prev color with shadow fading) */
    @keyframes hover-out {
      0% {
        color: black;
        text-shadow: 0 0 30px #850000, 0 0 10px #110402, 0 0 5px #110402;
      }
      50% {
        color: black;
        text-shadow: 0 0 20px #850000, 0 0 5px #110402, 0 0 3px #110402;
      }
      100% {
        color: ''; /* return to prev state? */
      }
    }
html:

<!doctype html>
<html>

<head>

<script type="text/javascript" src="CC-xxxCSS/CC-scripts.js" defer></script>

<script type="text/javascript" src="CC-xxxCSS/fragment-loader.js" defer></script>

<script type="text/javascript" src="CC-xxxCSS/base-navigation.js" defer></script>

<link rel="stylesheet" type="text/css" href="CC-xxxCSS/CC-styles.css">

<script src=".7.1.slim.js" integrity="sha256-UgvvN8vBkgO0luPSUl2s8TIlOSYRoGFAX4jlCIm9Adc=" crossorigin="anonymous"></script>



</head>


<body>

<div>
<p>
Fonts used on this website:

<p>
<ul>
    <li>
        <a href=".font">Black Castle MF</a>

    <li>
        <a href=".font">Blackletter HPLHS</a> 

    <li>
        <a href=".font">Chomsky</a> 

    <li>
        <a href="/">Trajan Pro</a>

    <li>
        <a href=".font">WW2 Blackletter HPLHS</a>

</ul>


</div>
</body>
</html>

I'm new to CSS and have been playing around with keyframe animations. I currently am using both CSS and JS in order to achieve the following:

  • apply a keyframe animation when a link is hovered over

  • apply a second animation when a link is no longer hovered over, but hasn't been clicked

  • apply a third animation when a link is clicked (and js to delay the nav for a moment so the animation can play, but only the first time it's clicked - after that it returns to normal link behavior)

My problem is that all my links are the a:visited color, even before they're clicked. I'm sure this has to do with how I set up the keyframe animations, but I can't figure out why - the hover-in and hover-out animations begin with the unvisited link color (hover in) and end with currentColor (hover out). If I change currentColor to the unvisited link color, the links ignore the a:visited color.

I don't think the on-click animation is at fault, since it only applies after a:active has been used (I also tried a:focus but it didn't seem to make a difference), but it does end on the visited link color.

(There are other issues, like the hover out animation playing on page load despite being paused, and the browser not remembering the visited state between page loads, but I can live with those if the link color behaves.)

Alternately, could it be the js doing this? I don't understand js very well at all, I had a lot of help with it.

ETA: I added animation-fill-mode: none; to the keyframe animations, but it didn't make a difference.

ETA2: fixed link color in css, but keyframe endstate for hover-out still defaults to black

ETA3: Fixed the default color, but there doesn't seem to be a pure-CSS solution to fade the hover-out from black to the current link color - it always defaults to either black, or the reddish color, rather than whatever condition the current link has, a:visited (black) or a:link (reddish).

document.querySelectorAll('a:not(.sitenav a):not(.footer a)').forEach(function (link) {
  let isAnimating = false;  // Flag to track if animation is already happening
  const linkId = link.href; // Use the link's href as a unique identifier
  let hasAnimatedOnce = localStorage.getItem(linkId) === 'true'; // Retrieve the value from localStorage

  link.addEventListener('click', function (e) {
    // If the link has been clicked before (stored in localStorage), allow normal behavior
    if (hasAnimatedOnce) {
      return; // Normal navigation will happen
    }

    // If animation is already happening, ignore the click
    if (isAnimating) {
      return;  // Do nothing if animation is in progress
    }

    // Prevent default navigation behavior to wait for the animation
    e.preventDefault();

    // Mark that the animation is starting
    isAnimating = true;

    // Reset the animation by removing it first (so it can be reapplied)
    this.style.animation = '';  // Remove the animation to allow it to play again

    // Trigger the animation by adding the animation property again
    this.style.animation = 'active-animation 3s forwards';  // Adjust timing as necessary

    // Store the linkElement to use later in the animationend handler
    this._linkElement = this;
  });

  // Separate animationend listener outside of click event listener
  link.addEventListener('animationend', function () {
    // If the link element was stored and animation is complete
    if (this._linkElement) {
      // Now navigate to the link's href after the animation finishes
      window.location.href = this._linkElement.href;

      // Reset the animation flag so the link can be clicked again
      isAnimating = false;

      // Mark that the link has been clicked and animated once
      hasAnimatedOnce = true;
      
      // Save the state to localStorage so the flag persists across sessions
      localStorage.setItem(linkId, 'true');
      
      // Clear the stored link element reference
      delete this._linkElement;
    }
  });
});
/* Base state for the link - initial color is reddish */
 
 
   a:link {
       position: relative;  /* Ensure the link is positioned */
        z-index: 1; /* Ensure the link is above other elements */
      color: #660e0e;  /* Start as reddish */
      pointer-events: auto;
      text-decoration: none;
      transition: color 0.4s ease;
    }
    
    
 a:not(.sitenav a):not(.footer a) {
  animation-play-state: paused; /* Prevent any animation from starting initially */
  pointer-events: auto;
  position: relative; /* Ensure the link is positioned */
  text-decoration: none;
}
 
 



/* Ensuring the link remains clickable during the animation */
    a:hover {
      pointer-events: auto; /* Ensures pointer events remain enabled */
    }



    /* Hover-in animation for normal hover effect (only when not clicked) */
    a:not(.sitenav a):not(.footer a):not(:active):hover {
      animation: hover-in 3s forwards; /* Add hover animation */
    }

 

/* Hover-out animation when the mouse leaves, but only if the link hasn't been clicked */
a:not(.sitenav a):not(.footer a):not(:active):not(:hover) {
  animation: hover-out 4s none cubic-bezier(.45,.05,.55,.95);
}


    /* visited link */
a:visited {
  color: black;
}



    
    /* selected link */
a:not(.sitenav a):not(.footer a):active {
  animation-name: active-animation;
  animation-duration: 3s;
  animation-fill-mode: none;
} 


 

    /* Active link animation (bright red -> black) */
    @keyframes active-animation {
      0% {
        color: #b80909; /* Start as bright red */
        text-shadow: 0 0 20px darkred, text-shadow: 0 0 5px brown;
      }
      10% {
        font-size: 1.02em; /* embiggen */
        color: #b80909;
        text-shadow: 0 0 20px darkred, text-shadow: 0 0 10px brown;
      }
      20% {
          color: black; /* Fade back to black */
      }
      30% {
        font-size: 1.04em;
      }
      60% {
        text-shadow: 0 0 10px darkred, 0 0 5px brown;
        font-size: ''; /* shrink again */
      }
      100% {
        text-shadow: 0 0 0px darkred, text-shadow: 0 0 0px brown;
        color: '';
      }
    }


    /* Hover-in animation (reddish to black with shadow) */
    @keyframes hover-in {
      0% {
        color: ''; /* Start as link or visited */
        text-shadow: none;
      }
      100% {
        color: black;
        text-shadow: 0 0 30px #850000, 0 0 10px #110402, 0 0 5px #110402;
      }
    }

    /* Hover-out animation (black to prev color with shadow fading) */
    @keyframes hover-out {
      0% {
        color: black;
        text-shadow: 0 0 30px #850000, 0 0 10px #110402, 0 0 5px #110402;
      }
      50% {
        color: black;
        text-shadow: 0 0 20px #850000, 0 0 5px #110402, 0 0 3px #110402;
      }
      100% {
        color: ''; /* return to prev state? */
      }
    }
html:

<!doctype html>
<html>

<head>

<script type="text/javascript" src="CC-xxxCSS/CC-scripts.js" defer></script>

<script type="text/javascript" src="CC-xxxCSS/fragment-loader.js" defer></script>

<script type="text/javascript" src="CC-xxxCSS/base-navigation.js" defer></script>

<link rel="stylesheet" type="text/css" href="CC-xxxCSS/CC-styles.css">

<script src="https://code.jquery/jquery-3.7.1.slim.js" integrity="sha256-UgvvN8vBkgO0luPSUl2s8TIlOSYRoGFAX4jlCIm9Adc=" crossorigin="anonymous"></script>



</head>


<body>

<div>
<p>
Fonts used on this website:

<p>
<ul>
    <li>
        <a href="https://www.1001freefonts/black-castle-mf.font">Black Castle MF</a>

    <li>
        <a href="http://www.dafont/blackletter-hplhs.font">Blackletter HPLHS</a> 

    <li>
        <a href="https://www.dafont/chomsky.font">Chomsky</a> 

    <li>
        <a href="https://freefontsfamily./trajan-pro-font-free/">Trajan Pro</a>

    <li>
        <a href="http://www.dafont/ww2blackletter.font">WW2 Blackletter HPLHS</a>

</ul>


</div>
</body>
</html>

Share Improve this question edited Feb 2 at 18:01 K-RAM asked Feb 2 at 12:57 K-RAMK-RAM 137 bronze badges 10
  • I've created a Runnable Snippet (currently non-functional since you missed to provide some HTML). Please edit your question in order to provide a minimal reproducible example. Read: How to Ask. – Roko C. Buljan Commented Feb 2 at 13:09
  • 2 As a first suggestion: never assign additional eventListeners inside an eventListener - this will incrementally create new listeners on every click. – Roko C. Buljan Commented Feb 2 at 13:11
  • Sorry! I've added the html now, I can upload to an actual server if needed, but not sure how to use a jfiddle? – K-RAM Commented Feb 2 at 13:22
  • 1 please, add the HTML right into the Runnable snippet, not as a code-block. – Roko C. Buljan Commented Feb 2 at 13:23
  • 1 You've got one "isAnimating" flag in your loop and it's shared among all the clickable links. That seems weird. – Pointy Commented Feb 2 at 13:24
 |  Show 5 more comments

1 Answer 1

Reset to default 0

The issue likely occurs because browser styles for :visited links have restrictions for security and privacy reasons. Here's why your a:link color might not be showing properly before a keyframe animation changes it to a:visited:

1️⃣ Browsers Restrict :visited Styling Browsers limit what can be styled on :visited links to prevent websites from detecting which links a user has visited. ✅ Allowed Properties: color, background-color, outline-color, border-color ❌ Not Allowed: opacity, transform, visibility, content, etc.

2️⃣ Keyframe Animation Might Not Work on :visited If your animation involves properties that aren't allowed on :visited, the browser may ignore or override the styles. Solution: Force Proper Styling Before & After Click

document.querySelectorAll("a").forEach(link => {
    link.addEventListener("click", function() {
        this.style.color = "purple"; // Simulate `:visited` manually
    });
});
a:link {
    color: blue;  /* Default color before click */
}

a:visited {
    color: purple;  /* Changes after click */
}

a:hover {
    color: red;  /* Hover effect */
}

a:active {
    color: green;  /* Click effect */
}

/* Keyframe animation example */
@keyframes fadeColor {
    0% { color: blue; }
    100% { color: purple; }
}

a:link, a:visited {
    animation: fadeColor 2s ease-in-out;
}

Browsers limit :visited styles, so animations might not work as expected. Ensure only color and background-color are animated. If necessary, use JavaScript to control styling dynamically.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论