Organizing tooltip data for two line charts with D3 and AngularJS

I've encountered an issue with a simple example involving 2 line charts with bullets. When hovering over the bullets, tooltips appear showing values that correspond to the y-axis frequency values. However, I'm struggling to determine which node (blue or red) I've hovered over in order to get the corresponding y-axis value (frequency or frequency2). Can anyone assist me in identifying this within the following function?

FUNCTION for tooltip:

var tip = d3.tip()
  .attr('class', 'd3-tip')
  .offset([-10, 0])
  .html(function(d) {
    return "<strong>Frequency:</strong> <span style='color:red'>" + d.frequency + "</span>";
 });

SNIPPET:

<html>

<head>

    <style>
        /* Styling for d3 tip */ 
        .d3-tip {
          line-height: 1;  font-weight: bold;  padding: 12px;  background: rgba(0, 0, 0, 0.8);  color: #fff;  border-radius: 2px;}
        
        .d3-tip:after {box-sizing: border-box;  display: inline;  font-size: 10px;  width: 100%;  line-height: 1;  color: rgba(0, 0, 0, 0.8);  content: "\25BC";  position: absolute;  text-align: center;}
        
        .d3-tip.n:after {margin: -1px 0 0 0;  top: 100%;  left: 0;}
    </style> 

    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.js"></script>



</head> 

<body ng-app="myApp" ng-controller="myCtrl"> 

    <svg></svg>

    <script>

        //module declaration 
        var app = angular.module('myApp',[]);

        //Controller declaration
        app.controller('myCtrl',function($scope){

            $scope.svgWidth = 800;//svg Width
            $scope.svgHeight = 500;//svg Height 

            //Data in proper format 
            var data = [
                  {"letter": "A","frequency": "5.01", "frequency2":"8.08"},
                  {"letter": "B","frequency": "7.80", "frequency2": "12.13"},
                  {"letter": "C","frequency": "15.35", "frequency2":"6.12"},
            ];

                //removing prior svg elements ie clean up svg 
                d3.select('svg').selectAll("*").remove();

                //resetting svg height and width in current svg 
                d3.select("svg").attr("width", $scope.svgWidth).attr("height", $scope.svgHeight);

                //Setting up of our svg with proper calculations 
                var svg = d3.select("svg");
                var margin = {top: 20, right: 20, bottom: 30, left: 40};
                var width = svg.attr("width") - margin.left - margin.right;
                var height = svg.attr("height") - margin.top - margin.bottom;

                var tip = d3.tip()
                  .attr('class', 'd3-tip')
                  .offset([-10, 0])
                  .html(function(d) {
                    return "<strong>Frequency:</strong> <span style='color:red'>" + d.frequency + "</span>";
                  });

                svg.call(tip);

                //Plotting our base area in svg in which chart will be shown 
                var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

                //X and Y scaling 
                var x = d3.scaleBand().rangeRound([0, width]).padding(0.4);
                var y = d3.scaleLinear().rangeRound([height, 0]);

                x.domain(data.map(function(d) { return d.letter; }));
                y.domain([0, d3.max(data, function(d) { return +d.frequency; })]);

                //Final Plotting 

                //for x axis 
                g.append("g")
                    .call(d3.axisBottom(x))
                    .attr("transform", "translate(0," + height + ")");

                //for y axis 
                g.append("g")
                    .call(d3.axisLeft(y))
                    .append("text").attr("transform", "rotate(-90)").attr("text-anchor", "end");

                //the line function for path 
                var lineFunction = d3.line()
                    .x(function(d) {return x(d.letter); })
                    .y(function(d) { return y(d.frequency); })
                    .curve(d3.curveCardinal);

                //defining the lines
                var path = g.append("path");

                //plotting lines
                path
                    .attr("d", lineFunction(data))
                    .attr("stroke", "blue")
                    .attr("stroke-width", 2)
                    .attr("fill", "none");

                g.selectAll('.circles1')
                    .data(data)
                    .enter().append('circle')
                    .attr('cx', function(d) {
                    return x(d.letter);
                    })
                    .attr('cy', function(d) {
                    return y(d.frequency);
                    })
                    .attr('r', 6)
                    .style("fill", "blue")
                    .on('mouseover', tip.show)
                    .on('mouseout', tip.hide);

            // ------------------ 2nd Iteration -----------------------// 

                //the line function for path 
                var lineFunction = d3.line()
                    .x(function(d) {return x(d.letter); })
                    .y(function(d) { return y(d.frequency2); })
                    .curve(d3.curveCardinal);

                //defining the lines
                var path = g.append("path");

                //plotting lines
                path
                    .attr("d", lineFunction(data))
                    .attr("stroke", "red")
                    .attr("stroke-width", 2)
                    .attr("fill", "none");

                g.selectAll('.circles1')
                    .data(data)
                    .enter().append('circle')
                    .attr('cx', function(d) {
                    return x(d.letter);
                    })
                    .attr('cy', function(d) {
                    return y(d.frequency2);
                    })
                    .attr('r', 6)
                    .style("fill", "red")
                    .on('mouseover', tip.show)
                    .on('mouseout', tip.hide);

        });

    </script> 

</body> 

</html>

RESULTS:

https://i.sstatic.net/f3BBN.png

https://i.sstatic.net/VswmY.png

Currently, both charts show the same frequency value. If I change the code in the following line:

   return "<strong>Frequency:</strong> <span style='color:red'>" + d.frequency + "</span>";

This results in both charts displaying the frequency values for the red chart. How can I determine which node (blue or red) I've hovered over?

Additionally, only xlabel and two y-axis frequency values are sent to the function, without information regarding which chart has been hovered over.

https://i.sstatic.net/4jqTH.png

Answer №1

Here are three possible solutions:

1) Implement two separate tooltips. One tooltip will display data related to frequency, while the other will show data associated with frequency2. You can control the visibility of each tooltip by adding show/hide functions to the corresponding data.

2) Another approach is to encapsulate the tip.show functions and provide only the relevant data to be displayed:

// Displaying data for the blue line
g.selectAll('...')
  .on('mouseover', function(d) {
    tip.show({frequency: d.frequency})
  })

// Showing data for the red line
g.selectAll('...')
  .on('mouseover', function(d) {
    tip.show({frequency: d.frequency2})
  })

3) To ensure that only pertinent data is passed to the tooltip based on the active circle, you can divide your data before binding it to the charts:

var blueData = data.map(function(d) {
  return {
    letter: d.letter,
    frequency: d.frequency
  };
})

var redData = data.map(function(d) {
  return {
    letter: d.letter,
    frequency: d.frequency2
  };
})

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

The customized message for parsley's minimum length is not being applied properly when passed as an option during binding

When attempting to bind the Parsley script to an input field, I am encountering an issue with the minlength message not displaying my custom one. Instead, the default error message from Parsley is being shown. I've tried adjusting the code but it does ...

Challenges with displaying the appropriate user interface in the dashboard according to different roles

My current project involves rendering different UI components based on selected roles such as brands, agency, or influencer. However, despite my efforts to implement the logic for this functionality, the correct UI is not being loaded and I'm struggli ...

Guide to utilizing sections within Angular

Planning to develop a website using Asp.net WebAPI and AngularJs. Seeking advice on how to separate the admin area from the user interface of the site. In my previous project, I utilized the MVC area to create an admin section. A friend recommended crea ...

Having issues with opening an exported Excel file using ExcelJS

While working on my JS project, I utilized the ExcelJS library. Everything was functioning smoothly with English characters. However, when I switched the language of my application to French and attempted to export the content as an Excel sheet, I encounte ...

What is the best way to activate a modal in the parent component when a button is clicked in the child component?

Currently I have the following setup: panelHeader.vue (which is a child component) index.vue (the parent component where my main list view is) panelHeader.vue <template> <v-row> <div class="panelHeader"> ...

Certain images are not being retrieved by Express on Linux, although they are functioning properly on Windows

When using a template to display HTML code, everything works smoothly on Windows. However, when transferring the code to a Linux machine, an issue arises - "Cannot GET /SmallVacImages/1.jpg". It seems that the index.html file can load images from the publi ...

Tips for sending arguments to react-highcharts?

While browsing through the documentation, I noticed that I can pass config in a certain way: render() { let config = this.config; return ( <div className="column"> <div className="ui segment"> ...

Alerts being used by Jquery Validation Engine to showcase errors

In the validation engine, my goal is to have error messages appear in a JavaScript alert box instead of as small tooltips right next to the input fields. $("#main_form").bind("jqv.form.result", function(event, errorFound) { if(errorFound) aler ...

PHP captures the checkbox value through the $_POST method, whereas jQuery removes the checked class from it

I currently have a form with 2 checkboxes: Registered and Not Registered. If the Registered checkbox is ticked, then 2 additional options should appear: Active and Not Active. If the Registered checkbox is unticked, both the Active and Not Active options ...

Encountering an error with code 'MODULE_NOT_FOUND' while attempting to deploy a bot on Heroku

Can anyone assist me with deploying my Twitter Bot on Heroku? I've configured the Twitter keys in the Heroku Vars, ensured the package.json is correct, and searched on Google for a solution, but I'm still facing issues. I'm not sure where I& ...

Developing a React-based UI library that combines both client-side and server-side components: A step-by-step

I'm working on developing a library that will export both server components and client components. The goal is to have it compatible with the Next.js app router, but I've run into a problem. It seems like when I build the library, the client comp ...

Unexpected behavior in Firefox occurs with PreloadJS when trying to preload elements that are added to the page after the initial load

I'm encountering an issue with the behavior of PreloadJS specifically on Firefox. It's surprising to me that no one else seems to have experienced this problem before as I couldn't find any similar descriptions online. Perhaps I am simply ov ...

Executing a JavaScript function using evaluateJavaScript(..) to retrieve information from a text file stored in the application's bundle

The initial part is functioning correctly, indicating that the WKWebview object can successfully call the javascript function. The issue seems to be within the javascript code itself. The purpose of the javascript function being called is to simply read f ...

Three Js - The Ultimate Database for 3D Models

Greetings, all. I am currently in the process of developing a website that utilizes Three.js. I am seeking recommendations for a platform or online database where I can store both the 3D model file and the website database together, if feasible. Your assi ...

The function for the Protractor promise is not defined

UPDATE: After some troubleshooting, I believe I have identified the issue causing it not to work. It seems that I am unable to pass arguments along when calling flow.execute(getSpendermeldung). Does anyone have a better solution than wrapping the ApiCall i ...

JS/Apps Script: Passing object and its keys as function parameters

When working with Google Apps Script, I have a specific task that involves looping through data and writing only certain keys to a sheet. I want this looping operation to be done in a separate function rather than directly in the main function, as it will ...

Animation of scroll progress in Next.js and Framer Motion

I am attempting to create a viewport scroll progress circle using Framer Motion in a Next.js application, incorporating Tailwind CSS. I referred to the example code provided by Framer Motion here:https://codesandbox.io/s/framer-motion-viewport-scroll-and-s ...

The container is accumulating excess margin

Here is the HTML code I am working with: <!DOCTYPE html> <html> <head> <meta charset="US-ASCII"> <link rel="stylesheet" type="text/css" href="css/index.css" > <title>Best company in the USA</title> < ...

Utilizing a Firebase function with Angular

I created the following function: retrieveLikedProperties(): AngularFirestoreCollection<any> { return this.afs.collection('users', ref => ref.where('uid', '==', this._auth.currentUserId) .where(&a ...

What is the reason that dates are not exported correctly in Laravel using jQuery data table when the month is two digits and the file format is EXCEL?

I am trying to export data as an EXCEL file from the view, everything is working fine except for the date format. Here is the code I have tried: $('#get_hourly_report').click(function () { var id = $('#teacher').val(); ...