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

Next.js 13中如何修改SVG属性值并添加节点?

在Next.js 13中使用SVGR修改SVG内容与添加节点

问题背景

我在Next.js 13项目里用SVGR把SVG作为组件加载,代码如下:

import CvSvg from './../../public/image.svg'

export default function Home() {
    return (
        <div className="flex flex-col min-h-screen">
            <CvSvg />
        </div>
    )
}

SVG里包含如下tspan元素:

<tspan
         sodipodi:role="line"
         style="font-size:4.93889px;fill:#000000;stroke:none;stroke-width:0.264583;stroke-opacity:1"
         x="79.525864"
         y="29.664894"
         id="tspan59991">message
</tspan>

我想实现两个需求:一是修改该tspan的文本内容、位置、颜色等属性;二是在tspan节点后添加新元素(比如一条线)。我尝试了DOM操作的代码,但线条并未显示:

import { useEffect, useState } from 'react';
import SVG from './public/image.svg'

function Home() {
  const [svgDoc, setSvgDoc] = useState(null);

  useEffect(() => {
    fetch(SVG)
      .then(response => response.text())
      .then(data => {
        const parser = new DOMParser();
        const svgDoc = parser.parseFromString(data, "image/svg+xml");

        const text = svgDoc.querySelector('#tspan59991');

        const line = svgDoc.createElementNS('http://www.w3.org/2000/svg', 'line');
        line.setAttribute('x1', '79.525864');
        line.setAttribute('y1', '30.664894');
        line.setAttribute('x2', '90');
        line.setAttribute('y2', '30.664894');
        line.setAttribute('style', 'stroke:#000000;stroke-width:1');

        text.parentNode.appendChild(line);
        setSvgDoc(svgDoc.documentElement.outerHTML);
      });
  }, []);

  return (
    <div className="flex flex-col min-h-screen">
      <div dangerouslySetInnerHTML={{ __html: svgDoc }} />
    </div>
  );
}

export default Home;

解决方案

一、通过SVGR组件特性修改(推荐)

SVGR会将SVG转换为React组件,你可以直接利用组件的props或ref进行操作,无需复杂的DOM解析:

1. 修改tspan的文本、颜色与位置

import { useRef, useEffect } from 'react';
import CvSvg from './../../public/image.svg';

export default function Home() {
  const tspanRef = useRef(null);

  useEffect(() => {
    if (tspanRef.current) {
      // 修改文本内容
      tspanRef.current.textContent = "新的自定义文本";
      // 修改文字颜色(覆盖原style的fill属性)
      tspanRef.current.style.fill = "#ff4444";
      // 修改位置坐标
      tspanRef.current.setAttribute('x', '85');
      tspanRef.current.setAttribute('y', '35');
    }
  }, []);

  return (
    <div className="flex flex-col min-h-screen">
      {/* 通过svgRef获取SVG内部元素的引用 */}
      <CvSvg svgRef={(svgElement) => {
        if (svgElement) tspanRef.current = svgElement.querySelector('#tspan59991');
      }} />
    </div>
  );
}

2. 给SVG添加新节点(比如线条)

直接通过组件的children插槽插入新的SVG元素,或者用createPortal指定插入位置:

import { useRef } from 'react';
import { createPortal } from 'react-dom';
import CvSvg from './../../public/image.svg';

export default function Home() {
  const tspanParentRef = useRef(null);

  return (
    <div className="flex flex-col min-h-screen">
      <CvSvg svgRef={(svgElement) => {
        if (svgElement) tspanParentRef.current = svgElement.querySelector('#tspan59991').parentNode;
      }} />
      {/* 使用createPortal将线条插入到tspan的父节点下 */}
      {tspanParentRef.current && createPortal(
        <line
          x1="79.525864"
          y1="30.664894"
          x2="90"
          y2="30.664894"
          style={{ stroke: "#000000", strokeWidth: 1 }}
        />,
        tspanParentRef.current
      )}
    </div>
  );
}

二、修复你尝试的DOM操作代码

你之前的代码线条不显示,主要是因为使用outerHTML转换丢失了部分SVG上下文,直接将解析后的SVG元素插入DOM即可:

import { useEffect, useRef } from 'react';
import SVG from './public/image.svg';

function Home() {
  const svgContainerRef = useRef(null);

  useEffect(() => {
    fetch(SVG)
      .then(response => response.text())
      .then(data => {
        const parser = new DOMParser();
        const svgDoc = parser.parseFromString(data, "image/svg+xml");
        const text = svgDoc.querySelector('#tspan59991');

        const line = svgDoc.createElementNS('http://www.w3.org/2000/svg', 'line');
        line.setAttribute('x1', '79.525864');
        line.setAttribute('y1', '30.664894');
        line.setAttribute('x2', '90');
        line.setAttribute('y2', '30.664894');
        line.setAttribute('stroke', '#000000');
        line.setAttribute('stroke-width', '1');

        text.parentNode.appendChild(line);
        // 直接将SVG元素添加到容器,而非转换为HTML字符串
        svgContainerRef.current.appendChild(svgDoc.documentElement);
      });
  }, []);

  return (
    <div className="flex flex-col min-h-screen">
      <div ref={svgContainerRef} />
    </div>
  );
}

export default Home;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 11:35:39