参考这个方法,但不想修改d3 https://gist.github.com/biovisualize/373c6216b5634327099a

虽然也绕了点弯,但还算很快了,比较满意,也学到了,记下来。

问题描述:希望在选择集操作时,在当前节点下挂新节点

d3.select("xx").each(function(d){

                  // do some thing

});

约束条件:

1 不是用d3的方式append,而是直接从别处的字符串形式的svg片段"<g>...</g>"。其实,这个片段是在这个函数现场,由别的非d3工具根据当前绑定的数据d生成的。

2 方法要兼容node和browser端

——业务和需求(问题域)是清晰的,难点在技术上(解空间)的约束条件。

把“业务”语言,一点点翻译成解空间的“技术”语言。

所以最初始的思路还是"如何把大象放进冰箱"的套路:

1 parse str_svg成当前dom中的某物(node?SVGelement?)

2 然后把这个东西挂到当前选择集/Element/Node下面。

参考上面的答案和别人家的做法,1 主要是2步

1 用DOMParser 把str_svg 解析掉,但返回的是一个新dom

2 依赖adoptNode,把新dom中的element 过继到d3选择集所在的dom,

2相对简单,对d3这个场景
function (d) {
// parse and get node1
this.appendChild(node1);
}

直接一句appendChild(node1)完事。注意选择集.each匿名回调函数中this,表示当前选中SVGElement。

这个文档里说得明白http://rajapradhan.com/blogs/d3-js-v4-essentials/d3-selections/

继续分析1,DOMParser的常见用法是

new DOMParser()

但这在浏览器端没问题,在node+jsdom下,会报错。不满足约束条件2了。

仔细看了jsdom的github,这个是在2016年才加入的。在jsdom模式,这样创建:

new window.DOMParser();

这里就清楚了,DOMParser位于当前dom的window 下。

但这里约束1又来了,我们是在d3的回调函数中,只有选择集和选择集对应的Element this

对浏览器没问题,对node+jsdom环境:没有当前dom文档的window对象。

如果不想给层层函数开天窗,传入window,(修改N个函数的接口),那么就引出新的问题:

如何从d3 selection or SVGElement  get 当前dom的 window?

老问题没解决,又层层嵌套依赖,遇到新问题。这就是技术工作的常态。急不得,习以为常就好。

有点像评书,原问题本来是突围、破阵、闯关,然后解决方案是需要去XX地方搬兵,请YY高人;结果请人过程中又扯远了,遇到很多问题。

——层层嵌套的调用,什么时候才能层层返回啊?

直接去搜索,不太容易找到答案。但jsdom,我们知道是dom.window.document.Element的关系。

把这个问题 通过document做过渡,再分成2步就容易了:

//1 SVGElement-> document

const doc = element_dest.ownerDocument;

//2  document-> window

const window1 = doc.parentWindow || doc.defaultView;

这里问题和解决方案已经基本合一了,标志是说明意图的注释,已经基本和代码一致。没有注释必要了。

这里一旦突破,一切就都自然而然了,最终,搞2个函数:

function create_DOMParser_from_document(doc){
//统一获得DOMParser的方式,防止node和浏览器有区别
const window1 = doc.parentWindow || doc.defaultView;
return new window1.DOMParser();
} function append_str_xml_to_element(element_dest,str_xml){
//把str_xml字符串添加到element (node)上
//兼容浏览器和node,不使用全局变量 document window
//element-> document
const doc_dest = element_dest.ownerDocument;
const domparser = create_DOMParser_from_document(doc_dest);
//临时创建的dom2 doc2
const doc2 = domparser.parseFromString(str_xml, "text/xml");
//过继node到doc_dest
const node1 = doc_dest.adoptNode(doc2.documentElement);
//添加节点
element_dest.appendChild(node1);
}

使用时:

    d3.select("xxx")
.each(function (d) {
//得到需要添加的字符串形式的svg片段
const str_svg = some_function(d);
//this表示当前element 而 d表示绑定的数据
append_str_xml_to_element(this,str_svg);
});

这种方法,通过jsdom和浏览器dom中 dom window document element的回溯访问方式(element.ownerDocument,doc.parentWindow || doc.defaultView)的不变性,保证了行为在不同运行环境下的效果统一。

没得到这个方法的时候,还要判断代码运行在node还是browser,分而治之。这里就不对比了。

总结——从一个函数看软件工程:

而对外暴露的接口,只接受str实现细节完全隐藏,没有把DOMParser前后依赖的这些破事暴露出来。

1 从价值上看,函数的名字,参数入口和返回值,一定要把问题域描述清楚(需要依赖什么输入)。

尽量把全部依赖都清晰写在入口,“收窄接口”只是最终效果,“明确目的+减少外部依赖”才是意图。

2 从函数定义那行到函数body部分语句块,其实还有一道隐形的鸿沟——问题域->解空间。

必须要有跨度。在能实现的前提下,越不相干,跨度越大,越有价值。

最好是

函数名人话(领域问题),

函数体外星咒语(解空间魔法)

3 body实现部分之间,尽量跨度小,关系尽量紧密,尽量没有冗余。每一行都要在这个领域最简单。

我喜欢用军队组织的层级编成来比喻代码:

每个源代码文件200-400行,大概相当于分队的管理机构。

每个函数100行以内,大概相当于1个分队。想这次这样的小函数,10几行,相当于1个班。

每行语句相当于1个战士完成的任务。

源代码=作战计划

编码=参谋指定计划,

测试,调试= 推演协同动作计划

需要分解、翻译、parse的思路,最终把作战计划翻译成每个最笨的战士也能听懂完成的简单任务。

尽量挖掘、利用标准、接口上显式的不变性,不要隐式依赖编译器、浏览器环境的特殊行为。

具体到混乱邪恶的js世界,要吃透es6,w3c 标准,用标准化的方式做事。

d3 parse字符串形式的xml svg and append to element的更多相关文章

  1. 将字符串以用二进制流的形式读入XML文件

    其实将字符串写入XML文件本身并不复杂,这里只是写一些需要注意的地方,特别是编码格式,这里需要的是XML默认的编码方式是UTF-8,在对字符串进行编码的时候一定要注意, string strRecei ...

  2. d3可视化实战01:理解SVG元素特性

    一. SVG简介 ————————————————————————————————————————————————————————————————— SVG是一种和图像分辨率无关的矢量图形格式,它使用 ...

  3. 使用任意的输入流(InputStream)实例,包括字符串形式的文件路径或者 file:// 的 URL 形式的文件路径来配置

    mybatis – MyBatis 3 | 入门 http://www.mybatis.org/mybatis-3/zh/getting-started.html 从 XML 中构建 SqlSessi ...

  4. divmod(a,b)函数是实现a除以b,然后返回商与余数的元组、eval可以执行一个字符串形式的表达式、exec语句用来执行储存在字符串或文件中的Python语句

    #!/usr/bin/env python a = 10/3 print(a) #divmod计算商与余数 r = divmod(10001,20) print(r) #eval可以执行一个字符串形式 ...

  5. org.hibernate.HibernateException: Could not parse configuration: /hibernate.cfg.xml

    org.hibernate.HibernateException: Could not parse configuration: /hibernate.cfg.xml at org.hibernate ...

  6. 关于Could not parse configuration: /hibernate.cfg.xml的问题

    第一次在eclipse上配置hibernate,问题百出啊,比如下面的org.hibernate.HibernateException: Could not parse configuration: ...

  7. 字符串json转换为xml xml转换json

    原文:字符串json转换为xml xml转换json // To convert an XML node contained in string xml into a JSON string XmlD ...

  8. xml字符串转对象xml文件转对象

    判断是否是ie浏览器和非ie浏览器的方法有多种,在此只介绍用例中的方法: 1.解析xml字符串,得到xml对象的方式: function createXml(str){ if(document.all ...

  9. Function:html结构转字符串形式显示

    //Html结构转字符串形式显示 支持<br>换行 function ToHtmlString(htmlStr) { return toTXT(htmlStr).replace(/\&am ...

随机推荐

  1. Golang生成区间随机整数

    package main import ( "fmt" "math/rand" "time" ) func main() { rand.Se ...

  2. P3157 [CQOI2011]动态逆序对(树状数组套线段树)

    P3157 [CQOI2011]动态逆序对 树状数组套线段树 静态逆序对咋做?树状数组(别管归并QWQ) 然鹅动态的咋做? 我们考虑每次删除一个元素. 减去的就是与这个元素有关的逆序对数,介个可以预处 ...

  3. HashMap的实现原理总结

    HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象. 当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到b ...

  4. git-tag 标签相关操作

    标签可以针对某一时间点的版本做标记,常用于版本发布. 列出标签 $ git tag # 在控制台打印出当前仓库的所有标签$ git tag -l ‘v0.1.*’ # 搜索符合模式的标签 打标签 gi ...

  5. body-parser 用法

    1.下载 body-parser 模块  :   npm install body-parser 2.require body-parser 模块(引入),并用一个变量接收(此处栗子变量为 bodyp ...

  6. mtr 命令

    mtr命令的使用: -r  --report  以报告的方式发布监测的结果 -s 30   指定发送包的大小  这个随意   按照自己的需求 -i 10  设置icmp协议返回包的时间 -n  no- ...

  7. media静态文件统一管理 操作内存的流 - StringIO | BytesIO PIL:python图片操作库 前端解析二进制流图片(了解) Admin自动化数据管理界面

    一.media ''' 1. 将用户上传的所有静态文件统一管理 -- settings.py -- MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 2. 服务 ...

  8. linux --- 2.常用命令 , python3, django安装

    一.常用命令 1.常识命令 ① w      显示终端连接数 ②pwd    我在哪 ③whoami      我是谁 ④which 命令        找到命令的绝对路径 2.linux 命令行的组 ...

  9. Restful framework【第四篇】视图组件

    基本使用 -view的封装过程有空可以看一下 -ViewSetMixin # ViewSetMixin 写在前面,先找ViewSetMixin的as_view方法 # 用了ViewSetMixin , ...

  10. 12.27 cf div3 解题报告

    12.27 cf div3 解题报告 wxy.wxy,带上分拉,全场做了个无脑小白 比赛场地 A: T1,跟着模拟就好了 B: sort一遍之后 去除的数一定是a[1]或者a[n] 比较去除谁小就输出 ...