You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单个HTML页面中实现D3.js v4多幅独立图表

嘿,我来给你详细讲讲怎么在同一个HTML页面里实现多个独立的D3.js v4图表(柱状图+折线图),核心就是把每个图表的逻辑完全隔离开,避免变量冲突和渲染干扰。下面是实操方案:

实现思路

核心是给每个图表分配独立的DOM容器,并用单独的函数封装各自的渲染逻辑,让变量、数据、渲染流程完全独立。

1. 先搭好HTML结构

给每个图表准备专属的容器,用ID区分开,方便D3精准定位:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Multiple D3.js Charts</title>
    <!-- 引入D3.js v4 -->
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
    <!-- 柱状图容器 -->
    <div class="chart-box" id="bar-chart"></div>
    <!-- 折线图容器 -->
    <div class="chart-box" id="line-chart"></div>
</body>
</html>

2. 基础样式配置

给容器设置固定尺寸,避免图表挤在一起:

.chart-box {
    width: 600px;
    height: 400px;
    margin: 30px auto;
    border: 1px solid #eee;
    padding: 10px;
}

3. 封装独立的图表初始化函数

每个图表的逻辑都放在单独的函数里,内部变量完全隔离,不会互相影响:

柱状图初始化函数

function drawBarChart(containerId, chartData) {
    // 获取容器尺寸
    const container = d3.select(`#${containerId}`);
    const width = container.node().clientWidth;
    const height = container.node().clientHeight;
    const margin = { top: 20, right: 20, bottom: 35, left: 40 };

    // 创建专属SVG
    const svg = container.append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", `translate(${margin.left}, ${margin.top})`);

    // 计算可用区域尺寸
    const innerWidth = width - margin.left - margin.right;
    const innerHeight = height - margin.top - margin.bottom;

    // 定义比例尺(仅当前图表可用)
    const xScale = d3.scaleBand()
        .domain(chartData.map(item => item.category))
        .range([0, innerWidth])
        .padding(0.1);

    const yScale = d3.scaleLinear()
        .domain([0, d3.max(chartData, item => item.value)])
        .range([innerHeight, 0]);

    // 添加坐标轴
    svg.append("g")
        .attr("transform", `translate(0, ${innerHeight})`)
        .call(d3.axisBottom(xScale))
        .selectAll("text")
        .attr("font-size", "12px");

    svg.append("g")
        .call(d3.axisLeft(yScale))
        .selectAll("text")
        .attr("font-size", "12px");

    // 绘制柱子
    svg.selectAll(".bar")
        .data(chartData)
        .enter()
        .append("rect")
        .attr("class", "bar")
        .attr("x", item => xScale(item.category))
        .attr("y", item => yScale(item.value))
        .attr("width", xScale.bandwidth())
        .attr("height", item => innerHeight - yScale(item.value))
        .attr("fill", "#27ae60");
}

折线图初始化函数

function drawLineChart(containerId, chartData) {
    const container = d3.select(`#${containerId}`);
    const width = container.node().clientWidth;
    const height = container.node().clientHeight;
    const margin = { top: 20, right: 20, bottom: 35, left: 40 };

    const svg = container.append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", `translate(${margin.left}, ${margin.top})`);

    const innerWidth = width - margin.left - margin.right;
    const innerHeight = height - margin.top - margin.bottom;

    // 定义时间比例尺和线性比例尺
    const xScale = d3.scaleTime()
        .domain(d3.extent(chartData, item => item.date))
        .range([0, innerWidth]);

    const yScale = d3.scaleLinear()
        .domain([0, d3.max(chartData, item => item.value)])
        .range([innerHeight, 0]);

    // 创建折线生成器
    const lineGenerator = d3.line()
        .x(item => xScale(item.date))
        .y(item => yScale(item.value));

    // 添加坐标轴
    svg.append("g")
        .attr("transform", `translate(0, ${innerHeight})`)
        .call(d3.axisBottom(xScale).ticks(5))
        .selectAll("text")
        .attr("font-size", "12px");

    svg.append("g")
        .call(d3.axisLeft(yScale))
        .selectAll("text")
        .attr("font-size", "12px");

    // 绘制折线
    svg.append("path")
        .datum(chartData)
        .attr("fill", "none")
        .attr("stroke", "#3498db")
        .attr("stroke-width", 2)
        .attr("d", lineGenerator);

    // 添加数据点
    svg.selectAll(".dot")
        .data(chartData)
        .enter()
        .append("circle")
        .attr("class", "dot")
        .attr("cx", item => xScale(item.date))
        .attr("cy", item => yScale(item.value))
        .attr("r", 4)
        .attr("fill", "#3498db");
}

4. 模拟数据并初始化图表

页面加载完成后,分别调用两个函数,传入各自的数据和容器ID:

// 柱状图模拟数据
const barData = [
    { category: "产品A", value: 45 },
    { category: "产品B", value: 68 },
    { category: "产品C", value: 32 },
    { category: "产品D", value: 75 }
];

// 折线图模拟数据
const lineData = [
    { date: new Date(2023, 0, 1), value: 22 },
    { date: new Date(2023, 1, 1), value: 48 },
    { date: new Date(2023, 2, 1), value: 35 },
    { date: new Date(2023, 3, 1), value: 56 },
    { date: new Date(2023, 4, 1), value: 42 }
];

// 页面加载完成后渲染图表
document.addEventListener("DOMContentLoaded", () => {
    drawBarChart("bar-chart", barData);
    drawLineChart("line-chart", lineData);
});

关键注意点

  • 作用域隔离:所有图表相关的变量(svg、比例尺、坐标轴等)都定义在函数内部,不会污染全局作用域。
  • 独立容器:每个图表对应专属的DOM节点,D3只会操作自己容器内的元素,不会干扰其他图表。
  • 数据独立:各自传入不同的数据源,完全互不影响。
  • 可扩展性:如果需要添加更多图表,只需要复制类似的函数,修改渲染逻辑即可。

内容的提问来源于stack exchange,提问作者prakash suryavanshi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:26:36