简体   繁体   中英

How to coordinate Rickshaw.js, d3.js with jQuery

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" class="block block-condensed no-margin {{ getDateRangeKey(getMainControlsSelections()->dateRange) }}"
             data-venue="{{ $mainControlsData->venue ? $mainControlsData->venue->id : '' }}"
        >
            <div class="app-heading">
                <div class="title">
                    <h2>{{ trans('app/heatmap.map.title') }}</h2>
                    <p>{{ trans('app/heatmap.map.subtitle') }}</p>
                </div>
                <div class="heading-elements">
                    <div class="{{ $mainControlsData->venue && $mainControlsData->venue->spotData && count($mainControlsData->venue->spotData->floors) > 1 && !$mainControlsData->floor ? '' : 'hidden' }}">
                        <label>
                            <span class="fa fa-object-ungroup"></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 class="block-content">

                <div class="row">

                    <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 class="left"></div>-->
                        <div class="right">
                            <div class="slider-container"></div><!-- /.slider-container -->
                        </div>
                    </div>

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

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

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

Anyone have a clue?

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. I needed to get the upgraded version of d3 without breaking the other functioning uses of the d3 code. I did a lot of research on this and found that the rickshaw.js repo had been abandoned a few years back. I found a fork that included a d3 v4 updgrade and was in the process of sorting out how best to upgrade the public/assets/app/vendor version of rickshaw with the v4 upgrade to d3 and avoid breaking changes to the legacy charting in the site. Then I stumbled upon what I consider the perfect solution in this case, which is the inclusion of two different versions of d3 in one page. That solution is found here: https://chewett.co.uk/blog/2021/how-to-load-multiple-d3-versions-at-once/ After implementing that my legacy charts are working fine and my new bubbles are working.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM