skip to Main Content

I’m trying to change the color of other slices when hovering one of them, but is a little buggy sometimes and I’m trying to understand why.

As shown in the example, there is a flickering when hovering from one slice to another and the colors don’t change right away, and also when leaving the slice the colors should reset, but it doesn’t work sometimes.

const colors = ["red", "green", "blue"];
const colorsRGBA = ["rgba(255,0,0,.25)", "rgba(0,255,0,.25)", "rgba(0,0,255,.25)"];

new Chart(document.getElementById("doughnut"), {
  type: "doughnut",
  data: {
    labels: ["A", "B", "C"],
    datasets: [
      {
        data: [1, 2, 3],
        backgroundColor: ["red", "green", "blue"],
        hoverBackgroundColor: ["red", "green", "blue"]
      }
    ]
  },
  options: {
    plugins: {
      tooltip: {
        enabled: false
      },
      legend: {
        display: false
      }
    },
    onHover(event, elements, chart) {
      if (event.type === "mousemove") {
        if (elements.length) {
          chart.getDatasetMeta(0).data.forEach((data, index) => {
            if (index !== elements[0].index) {
              data.options.backgroundColor = colorsRGBA[index];
            }
          });
        } else {
          chart.getDatasetMeta(0).data.forEach((data, index) => {
            data.options.backgroundColor = colors[index];
          });
        }
      }
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
<div style="height: 350px">
  <canvas id="doughnut"></canvas>
</div>

2

Answers


  1. I think your code is very good, but you need to add some simple code to achieve your goal.
    This is my suggestion.
    I hope this code snippet helps you.

    const colors = ["red", "green", "blue"];
    const colorsRGBA = [
      "rgba(255,0,0,.25)",
      "rgba(0,255,0,.25)",
      "rgba(0,0,255,.25)",
    ];
    
    new Chart(document.getElementById("doughnut"), {
      type: "doughnut",
      data: {
        labels: ["A", "B", "C"],
        datasets: [
          {
            data: [1, 2, 3],
            backgroundColor: colors,
            hoverBackgroundColor: colors,
          },
        ],
      },
      options: {
        plugins: {
          tooltip: {
            enabled: false,
          },
          legend: {
            display: false,
          },
        },
        onHover(event, elements, chart) {
          if (event.type === "mousemove") {
            if (elements.length) {
              chart.getDatasetMeta(0).data.forEach((data, index) => {
                if (index !== elements[0].index) {
                  data.options.backgroundColor = colorsRGBA[index];
                } else {
                  data.options.backgroundColor = colors[index];
                }
              });
            } else {
              chart.getDatasetMeta(0).data.forEach((data, index) => {
                data.options.backgroundColor = colors[index];
              });
            }
            chart.update();
          }
        },
      },
    });
    body {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 50vh;
        margin: 0;
        background-color: #f0f0f0;
      }
      
      #doughnut {
        width: 400px;
        height: 400px;
      }
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Doughnut Chart</title>
        <link rel="stylesheet" href="style.css" />
      </head>
      <body>
        <canvas id="doughnut"></canvas>
        <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
        <script src="script.js"></script>
      </body>
    </html>
    Login or Signup to reply.
  2. The problem is that your explicit setting of colors is "competing against" the default chart.js process by which the colors are set when the cursor enters and leaves elements.

    The only issue I can see, is that when the cursor is moved in one short fast shift from one element to another, the element that was left will be restored its color from the dataset (non-transparent), but that is done in an animation that will happen after your code has
    set the semitransparent color, so you end up with two elements with full color.

    The solution is to disable the colors animations;
    that can be done in the chart configuration at options:

       // .....
       options: {
          animations: {colors: false},
          plugins: {
             // .....
        
    

    You may want to also set at the canvas level a mouseOut event handler for the case the cursor is
    moved fast outside the canvas or the browser tab is changed through the keyboard while the cursor is
    on an element.

    Code snippet with these changes:

    const colors = ["red", "green", "blue"];
    const colorsRGBA = ["rgba(255,0,0,.25)", "rgba(0,255,0,.25)", "rgba(0,0,255,.25)"];
    
    const chart = new Chart(document.getElementById("doughnut"), {
       type: "doughnut",
       data: {
          labels: ["A", "B", "C"],
          datasets: [
             {
                data: [1, 2, 3],
                backgroundColor: ["red", "green", "blue"],
                hoverBackgroundColor: ["red", "green", "blue"]
             }
          ]
       },
       options: {
          plugins: {
             tooltip: {
                enabled: false
             },
             legend: {
                display: false
             }
          },
          animations: {colors: false},
          onHover(event, elements, chart) {
             if (event.type === "mousemove") {
                if (elements.length) {
                   chart.getDatasetMeta(0).data.forEach((data, index) => {
                      if (index !== elements[0].index) {
                         data.options.backgroundColor = colorsRGBA[index];
                      }
                   });
                } else {
                   chart.getDatasetMeta(0).data.forEach((data, index) => {
                      data.options.backgroundColor = colors[index];
                   });
                }
             }
          }
       }
    });
    document.getElementById("doughnut").addEventListener('mouseout', function(){
       const chart = Chart.getChart("doughnut");
       chart.getDatasetMeta(0).data.forEach((data, index) => {
          data.options.backgroundColor = colors[index];
       });
       chart.render();
    });
    <div style="height: 350px">
       <canvas id="doughnut"></canvas>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

    This fiddle contains mostly the same code except for the
    animations, where the opposite way is taken, setting the duration to 5 seconds so one
    can see the effect that I described.

    Please let me know if there are still undesired effects that I couldn’t detect.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search