如何创建多组不同数据集的Google Charts并解决回调传参问题?
解决Google可视化图表异步回调中元素被覆盖的问题
这个问题我之前也碰到过!核心就是循环里的异步操作和变量作用域的坑——当query.send()的异步回调执行时,循环早已经跑完了,全局变量或者用var声明的变量会被最后一次迭代的值覆盖,导致所有回调都用同一个元素。下面给你几种靠谱的解决方法:
方法一:用ES6的let创建块级作用域
把循环里的变量声明改成let,它会在每次循环迭代时创建一个独立的作用域,这样每个异步回调都会绑定当前迭代的元素,不会被后续迭代覆盖。修改后的代码如下:
let cwraggbgraphs = []; google.charts.load('current', {'packages':['corechart']}); google.charts.setOnLoadCallback(initializeData); function initializeData() { cwraggbgraphs = document.getElementsByClassName("cwraggbp"); // 把var改成let,让i和当前元素都拥有块级作用域 for (let i = 0; i < cwraggbgraphs.length; i++) { let cwraggbgraph = cwraggbgraphs.item(i); // 这里用let声明,每次迭代独立 var opts = {sendMethod: 'auto', csvColumns: ['string', 'number', 'number'], csvHasHeader: true}; var query = new google.visualization.Query(cwraggbgraph.dataset.cwraggbpSrc, opts); // 直接在send里传一个匿名函数,捕获当前的cwraggbgraph query.send(function(response) { handleQueryResponse(response, cwraggbgraph); }); } } // 修改handleQueryResponse,接收response和目标元素 function handleQueryResponse(response, targetElement) { console.log("Got response ", response); console.log("Using element ", targetElement); if (response.isError()) { alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage()); return; } let data = response.getDataTable(); let chart = new google.visualization.PieChart(targetElement); chart.draw(data, {width: 400, height: 240, is3D: true}); }
方法二:用立即执行函数(IIFE)创建独立作用域
如果你的环境还不能用ES6,这个方法兼容性更好。通过IIFE把当前迭代的元素封装到独立作用域里,确保回调能拿到正确的元素:
let cwraggbgraphs = []; google.charts.load('current', {'packages':['corechart']}); google.charts.setOnLoadCallback(initializeData); function initializeData() { cwraggbgraphs = document.getElementsByClassName("cwraggbp"); for (var i = 0; i < cwraggbgraphs.length; i++) { let cwraggbgraph = cwraggbgraphs.item(i); // 用IIFE包裹,把当前元素作为参数传入 (function(targetElement) { var opts = {sendMethod: 'auto', csvColumns: ['string', 'number', 'number'], csvHasHeader: true}; var query = new google.visualization.Query(targetElement.dataset.cwraggbpSrc, opts); query.send(function(response) { handleQueryResponse(response, targetElement); }); })(cwraggbgraph); } } function handleQueryResponse(response, targetElement) { console.log("Got response ", response); console.log("Using element ", targetElement); if (response.isError()) { alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage()); return; } let data = response.getDataTable(); let chart = new google.visualization.PieChart(targetElement); chart.draw(data, {width: 400, height: 240, is3D: true}); }
方法三:用forEach遍历元素
把HTMLCollection转成数组后用forEach遍历,forEach的每次迭代都会创建独立的作用域,天然避免变量共享问题,代码也更简洁:
google.charts.load('current', {'packages':['corechart']}); google.charts.setOnLoadCallback(initializeData); function initializeData() { // 把HTMLCollection转成数组 const cwraggbgraphs = Array.from(document.getElementsByClassName("cwraggbp")); cwraggbgraphs.forEach(function(targetElement) { var opts = {sendMethod: 'auto', csvColumns: ['string', 'number', 'number'], csvHasHeader: true}; var query = new google.visualization.Query(targetElement.dataset.cwraggbpSrc, opts); query.send(function(response) { handleQueryResponse(response, targetElement); }); }); } function handleQueryResponse(response, targetElement) { console.log("Got response ", response); console.log("Using element ", targetElement); if (response.isError()) { alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage()); return; } let data = response.getDataTable(); let chart = new google.visualization.PieChart(targetElement); chart.draw(data, {width: 400, height: 240, is3D: true}); }
核心思路都是让每个异步回调绑定到当前迭代的独立元素上,避免全局变量或共享作用域变量被覆盖的问题。
内容的提问来源于stack exchange,提问作者philolegein
相关产品推荐
相关产品推荐

