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

内联脚本调用正常,外部app.js调用D3.js碰撞函数报bottom属性错误

SVG文本碰撞避免功能的严格模式错误解决

问题场景

实现SVG文本元素碰撞避免时,内联脚本中的collision()函数运行正常,但将相同逻辑放到外部app.js文件,通过按钮事件监听器调用collisionAvoid()时,抛出错误:

TypeError: Cannot set property bottom of # which has only a getter

错误原因

getBoundingClientRect()返回的DOMRect是只读对象,无法直接修改它的bottom、top等属性:

  • 内联脚本未启用严格模式,浏览器会静默忽略对只读属性的修改,因此未报错;
  • 外部app.js开头声明了"use strict";,严格模式下会直接抛出属性修改错误。

解决方案

创建自定义的可修改对象,存储文本元素的位置信息,代替只读的DOMRect。更新文本的y属性后,同步更新自定义对象的对应值,用于后续碰撞检测。

修改后的代码

外部app.js代码

"use strict";
var bt = document.getElementById('btn');
bt.addEventListener('click', function(e){
  collisionAvoid();
}, false);

function collisionAvoid(){
  const svg = d3.select("svg");
  let hasCollisions = true;
  
  while (hasCollisions) {
    hasCollisions = false;
  
    const textElements = svg.selectAll("text");
  
    textElements.each(function () {
      const textElement = d3.select(this);
      // 创建自定义可修改的位置对象,复制DOMRect的属性
      const rect = {
        top: this.getBoundingClientRect().top,
        bottom: this.getBoundingClientRect().bottom
      };
  
      textElements.each(function () {
        if (this !== textElement.node()) {
          const otherRect = this.getBoundingClientRect();
  
          if (rect.bottom > otherRect.top && rect.top < otherRect.bottom) {
            const yOffset = Math.max(0, otherRect.bottom - rect.top + 5); // 添加间距
            textElement.attr("y", parseFloat(textElement.attr("y")) + yOffset);
            // 更新自定义对象的属性,而非只读的DOMRect
            rect.bottom += yOffset;
            rect.top += yOffset;
            hasCollisions = true;
          }
        }
      });
    });
  }
}

内联脚本(可选优化,统一规范)

function collision(){
  const svg = d3.select("svg");
  let hasCollisions = true;

  while (hasCollisions) {
    hasCollisions = false;

    const textElements = svg.selectAll("text");

    textElements.each(function () {
      const textElement = d3.select(this);
      // 同样使用自定义可修改对象
      const rect = {
        top: this.getBoundingClientRect().top,
        bottom: this.getBoundingClientRect().bottom
      };

      textElements.each(function () {
        if (this !== textElement.node()) {
          const otherRect = this.getBoundingClientRect();

          if (rect.bottom > otherRect.top && rect.top < otherRect.bottom) {
            const yOffset = Math.max(0, otherRect.bottom - rect.top + 5); // 添加间距
            textElement.attr("y", parseFloat(textElement.attr("y")) + yOffset);
            rect.bottom += yOffset;
            rect.top += yOffset;
            hasCollisions = true;
          }
        }
      });
    });
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:29:56