Search code examples
javascriptcssfrontenddom-eventsradial-gradients

Assigning gradient using JS fails on dynamically generated gradient


Assigning a gradient to an HTML element fails, if the string of CSS is generated in JS on runtime, but it does not if it's a string defined as constant. I want to generate a radial-gradient at the position of the cursor, but I simplified this example to only contain the lines of code required to cause the unwanted behaviour.

HTML:

<div class=buggy-gradient></div>
<div class=buggy-gradient></div>
<div class=buggy-gradient></div>

JS (not working):

let divs = document.getElementsByClassName('buggy-gradient');

for(i = 0; i < divs.length; i++){
  divs.item(i).addEventListener('mousemove', e => {
    let gradient = 'radial-gradient(circle at ' + e.clientX + ' ' + e.clientY + ', blue, red)';
    console.log(gradient);
    e.target.style.background = gradient;
  });
}

The console-log shows the CSS line as I want it, but assigning it apparently fails:

"radial-gradient(circle at 1044 325, blue, red)"

JS (working):

let divs = document.getElementsByClassName('buggy-gradient');

for(i = 0; i < divs.length; i++){
  divs.item(i).addEventListener('mousemove', e => {
    e.target.style.background = 'radial-gradient(circle at 0 0, blue, red)';
  });
}

I made a pen: https://codepen.io/stairjoke/pen/poNgKbm

As a Stack Snippet:

let divs = document.getElementsByClassName('buggy-gradient');

for(i = 0; i < divs.length; i++){
  divs.item(i).addEventListener('mousemove', e => {
    let gradient = 'radial-gradient(circle at ' + e.clientX + ' ' + e.clientY + ', blue, red)';
    console.log(gradient);
    e.target.style.background = gradient;
    // e.target.style.background = 'radial-gradient(circle at 0 0, blue, red)';
  });
}
div {
  height: 400px;
  width: 400px;
  background: radial-gradient(green, yellow);
  display: inline-block;
  margin: 5px;
}
<div class=buggy-gradient></div>
<div class=buggy-gradient></div>
<div class=buggy-gradient></div>


Solution

  • The problem is that you don't have any units on your values. 0 is a special value, it doesn't require any units, but all other values do.

    Adding px solves the problem:

    let gradient = 'radial-gradient(circle at ' + e.clientX + 'px ' + e.clientY + 'px, blue, red)';
    // −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^−−−−−−−−−−−−−−−−−−^^
    

    FWIW, I'd use a template literal:

    let gradient = `radial-gradient(circle at ${e.clientX}px ${e.clientY}px, blue, red)`;
    // −−−−−−−−−−−−^−−−−−−−−−−−−−−−−−−−−−−−−−−^^−−−−−−−−−^^^−^^−−−−−−−−−^^^−−−−−−−−−−−−^
    

    let divs = document.getElementsByClassName('buggy-gradient');
    
    for(i = 0; i < divs.length; i++){
      divs.item(i).addEventListener('mousemove', e => {
        let gradient = `radial-gradient(circle at ${e.clientX}px ${e.clientY}px, blue, red)`;
        console.log(gradient);
        e.target.style.background = gradient;
      });
    }
    div {
      height: 400px;
      width: 400px;
      background: radial-gradient(green, yellow);
      display: inline-block;
      margin: 5px;
    }
    <div class=buggy-gradient></div>
    <div class=buggy-gradient></div>
    <div class=buggy-gradient></div>