Home > Enterprise >  How to coordinate Rickshaw.js, d3.js with jQuery
How to coordinate Rickshaw.js, d3.js with jQuery

Time:12-06

I have a Laravel site which uses jQuery to load the data that is pulled from the database via the various controllers. I am trying to add a d3 visualization of a Bubble chart, but I'm running into conflicts and I'm not sure what is conflicting with what. There appears to be a conflict between the jQuery and d3 code. But also installed in the site is Rickshaw.js which is used to generate some dynamic time series graphs and Rickshaw is based on d3. I embedded a d3 script in my view file. So that it is using the latest version of d3 I included a link to the latest version before the d3 script:

<script src="https://d3js.org/d3.v6.js"></script>

However, when I console.log the d3 version, it says it is 3.5.17 which I suspect is the version included with the Rickshaw.js package although I can't find something that tells me what version is being used.

Before creating the jQuery code for pulling the data and passing it to the d3 script I tried passing the data directly to the view file from the controller and I got the d3 code working and a rendition of bubble charts. They would not update dynamically, though. I put together the jQuery scripts and at first had difficulty passing the data to the d3 script in the view file, but finally overcame that bug and the next issue I got was an error, "Uncaught TypeError: svg.append(...).data(...).selectAll(...).join(...).attr is not a function".

The d3 code I was using was from the https://d3-graph-gallery.com/ site. The error was from the setting of attributes of a node variable. Looking back at the original, I found a different version of that section of code, inserted that and the above error went away. Instead I got another error, "Uncaught TypeError: d3.drag is not a function" coming further down in the overall code.

I have not found a solution for that error. I don't know if it's a conflict between d3 and jQuery, a version conflict or the handling of the d3.drag() function is simply not correct. Does someone have any idea what I'm running up against?

Here is the jQuery code that pulls the data and provides it to the view file:

this.Load = function (data) {
                if (_xhr) {
                    _xhr.abort();
                    _xhr = null;
                }

                _this.SetLoading(true);
                _xhr = $.ajax({
                    url: $("meta[name='root']").attr("content")   '/app/heatmap/bubble',
                    type: 'POST',
                    headers: {
                        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                    },
                    data: {
                        date_id: data.dateRange.date,
                        venue_id: data.venue,
                        floor_id: data.floor,
                        zone_id: data.zone
                    },
                    dataType: 'JSON',
                    async: true,
                    cache: false,
                    error: function (jqXHR, textStatus, errorThrown) {
                        _this.SetLoading(false);
                    },
                    success: function (response) {
                        _this.SetLoading(false);
                        _this.SetKey(data.dateRange.key);
                        _this.Update(response);
                    }
                });
            };

            this.Update = function (data) {
                if (_.isUndefined(data) || _.isNull(data)) {
                    data = {};
                }

                if (this.BubbleChart && data) {
                    this.BubbleChart.Update(displayChart(data));
                }
            };

And here is the section of the view file that holds the d3 script and relevant HTML for rendering the charts.

<div id="heatmap-bubble" 
             data-venue="{{ $mainControlsData->venue ? $mainControlsData->venue->id : '' }}"
        >
            <div >
                <div >
                    <h2>{{ trans('app/heatmap.map.title') }}</h2>
                    <p>{{ trans('app/heatmap.map.subtitle') }}</p>
                </div>
                <div >
                    <div >
                        <label>
                            <span ></span>
                        </label>
                        <select id="mapControls-floor"
                                data-noneSelectedText="{{ trans('app/main.mainControls.floor.noneSelectedText') }}"
                                {{ count($mainControlsData->venue->spotData->floors) > 1 ? '' : 'disabled' }}
                        >
                            @if($mainControlsData->venue && isset($mainControlsData->venue->spotData->floors))
                                @foreach($mainControlsData->venue->spotData->floors as $floor)
                                    <option value="{{ $floor->id }}"{{ ($mainControlsData->floor && $mainControlsData->floor->id === $floor->id) ? ' selected' : '' }}>{{ $floor->name }}</option>
                                @endforeach
                            @endif
                        </select>
                    </div>
                </div>
            </div>

            <div >

                <div >

                    <div id="bubble-chart">
                        <span id="bubbleData"
                              data-type="text"
                              data-visitors
                        ></span>
                       
                        <script src="https://d3js.org/d3.v6.js"></script>
                        <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
                        <div id="bubble_viz"></div>
                        <script>

                            var displayChart = function(data) {

                            // set the dimensions and margins of the graph
                            const width = 700
                            const height = 460

                            // append the svg object to the body of the page
                            const svg = d3.select("#bubble_viz")
                                .append("svg")
                                .attr("width", width)
                                .attr("height", height)

                            var zones = [];
                            for (var i = 0, l = data.length; i < l; i  ) {
                                var obj = data[i];
                                zones[i] = obj.zone;
                            }

                            // Color palette for continents?
                            const color = d3.scale.ordinal()
                                .domain([1, 2, 3, 4, 5, 6,7])
                                .range(["#98ccd9", "#79b9c9", "#549eb0", "#6f939b", "#217185", "#cfe3e8", "#044e61"]);

                            // Size scale for venues
                            const size = d3.scale.linear()
                                .domain([0, 100])
                                .range([15,100])  // circle will be between 15 and 100 px wide

                            // create a tooltip
                            const Tooltip = d3.select("#bubble_viz")
                                .append("div")
                                .style("opacity", 0)
                                .attr("class", "tooltip")
                                .style("background-color", "#ebeef2")
                                .style("border", "solid")
                                .style("border-width", "2px")
                                .style("border-radius", "5px")
                                .style("padding", "5px")

                            // Three function that change the tooltip when user hover / move / leave a cell
                            const mouseover = function(event, d) {
                                Tooltip
                                    .style("opacity", 1)
                            }
                            const mousemove = function(event, d) {
                                Tooltip
                                    .html('<b>'   d.zone   '</b>'   "<br>"   d.value   " visitors")
                                    .style("left", (event.x/2-300)   "px")
                                    .style("top", (event.y/2-200)   "px")
                            }
                            var mouseleave = function(event, d) {
                                Tooltip
                                    .style("opacity", 0)
                            }

                            // Initialize the circle: all located at the center of the svg area
                            var node = svg.append("g")
                                .selectAll("circle")
                                .data(data)
                                .enter()
                                .append("circle")
                                .attr("class", "node")
                                .attr("r", d => size(d.value))
                                .attr("cx", width)
                                .attr("cy", height)
                                .style("fill", d => color(d.zone))
                                .style("fill-opacity", 0.8)
                                .attr("stroke", "black")
                                .style("stroke-width", 1)
                                .on("mouseover", mouseover) // What to do when hovered
                                .on("mousemove", mousemove)
                                .on("mouseleave", mouseleave)
                                .call(d3.drag() // call specific function when circle is dragged
                                    .on("start", dragstarted)
                                    .on("drag", dragged)
                                    .on("end", dragended));

                            let texts = svg.selectAll(null)
                                .data(data)
                                .enter()
                                .append('text')
                                .attr("text-anchor", "middle")
                                .text(d => d.zone)
                                .attr('color', 'black')
                                .attr('font-size', 15)

                            // Features of the forces applied to the nodes:
                            const simulation = d3.forceSimulation()
                                .force("center", d3.forceCenter().x(width / 2).y(height / 2)) // Attraction to the center of the svg area
                                .force("charge", d3.forceManyBody().strength(.1)) // Nodes are attracted one each other of value is > 0
                                .force("collide", d3.forceCollide().strength(.2).radius(function(d){ return (size(d.value) 3) }).iterations(1)) // Force that avoids circle overlapping

                            // Apply these forces to the nodes and update their positions.
                            // Once the force algorithm is happy with positions ('alpha' value is low enough), simulations will stop.
                            simulation
                                .nodes(data)
                                .on("tick", function(d){
                                    node
                                        .attr("cx", d => d.x)
                                        .attr("cy", d => d.y)
                                    texts
                                        .attr("cx", d => d.x)
                                        .attr("cy", d => d.y)
                                });

                            // What happens when a circle is dragged?
                            function dragstarted(event, d) {
                                if (!event.active) simulation.alphaTarget(.03).restart();
                                d.fx = d.x;
                                d.fy = d.y;
                            }
                            function dragged(event, d) {
                                d.fx = event.x;
                                d.fy = event.y;
                            }
                            function dragended(event, d) {
                                if (!event.active) simulation.alphaTarget(.03);
                                d.fx = null;
                                d.fy = null;
                            }
                            }
                        </script>

                    </div><!-- /#mapContainer -->

                    <div id="mapSlider">
                        <!--<div ></div>-->
                        <div >
                            <div ></div><!-- /.slider-container -->
                        </div>
                    </div>

                </div><!-- /.row -->

            </div><!-- /.block-content -->

        </div><!-- /#heatmap-bubble -->

Anyone have a clue?

CodePudding user response:

I have discovered that the issue is version conflict. The functionality I am looking for is not available in d3 v3.5.17. I have made progress tweaking the code with the current version. But now I need to simply get the version of d3 upgraded without breaking the other functioning uses of the code.

  • Related