D3基础01 - 对DOM和样式的核心操作

1,456 阅读4分钟

写在前面:

  1. 本文是单纯的个人学习总结和课堂笔记整理,大部分知识点来自“开课吧”线上直播课程,以及官方资料。
  2. 由于目前仍处于前端学习、入门阶段,因此对于知识的掌握和理解难免会有偏差,因此该文章仅仅是个人记录,请查阅者不要以此作为资料依据。

D3.js(D3或Data-Driven Documents)是一个使用动态图形进行资料可视化的JavaScript程序库。与W3C标准兼容,并且利用广泛实现的SVG、JavaScript和CSS标准,改良自早期的Protovis程序库。与其他的程序库相比,D3对视图结果有很大的可控性。D3是2011年面世的,同年的8月发布了2.0.0版。到2018年4月,D3已被更新到了5.5.0版。

D3.js已被数十万个网站使用,最常被运用在在线新闻网站呈现交互式图形、呈现数据资料的图表和呈现含有地理信息的资料。另外SVG的输出功能也使得D3.js能用于印刷出版物的绘制上。(来自wikipedia介绍)

总之,D3类似canvas可操作SVG,不同的是D3可以直接操作DOM。所以我们在这篇文章里面先一起看一下D3对DOM和样式的核心操作。

入门

可以通过npm安装完整依赖环境,也可以图省事,直接在HTML文件里面引入CDN。

<script src="https://lib.baomitu.com/d3/6.2.0/d3.js"></script>

成功引入之后我们就可以使用D3来进行绘图了。

  • D3.select() 方法选择绘图容器
  • .append() 方法添加对象
  • .attr() 方法添加属性
  • .on() 方法绑定事件及回调
<body>
    <!--svg容器对象-div-->
    <div id="main"></div> 
</body>
<script>
    /*需求:
    *   建立一个小球
    *   鼠标划入时,变色
    *   鼠标划出时,恢复原本颜色
    * */

    /*d3.select() 方法选择容器*/
    const main=d3.select('#main')

    /*向容器中添加svg*/
    const svg=main.append('svg')
        .attr('width','100%')
        .attr('height','100%')

    /*向svg中添加一个圆circle
    *    attr() 属性设置
    *       cx|cy 圆心位
    *       r 半径
    *       fill 填充颜色
    *       cursor 光标形状:pointer 小手
    * */
    const ball=svg.append('circle')
        .attr('cx',300)
        .attr('cy',300)
        .attr('r',200)
        .attr('fill','#00acec')
        .attr('cursor','pointer')

    /*用on方法监听鼠标划入图形事件mouseover
    *   attr 方法改变小球填充色fill
    * */
    ball.on('mouseover',()=>{
        ball.attr('fill','orange')
    })

    /*用on方法监听鼠标划出图形事件mouseout
    *   attr 方法改变小球填充色fill
    * */
    ball.on('mouseout',()=>{
        ball.attr('fill','#00acec')
    })

</script>

选择

因为D3是一种相对底层的JS库,很多语法看上去和原生JavaScript语法很想,比如说D3选择元素的方式就和原生JavaScript很想,有两种方式:

  1. 通过CSS选择器:class或者id
  2. 通过标签名:如p、div、li

我们来对比一下原生JavaScript选择器和D3选择器:

<script>
  	// 用原生js获取title元素
    const title=document.querySelector('#title')
    // select选择title元素
    d3.select(title).classed('sky',true)
    d3.select(title).classed('sky',false)
</script>

D3多选,其实叫全选我觉得更合适:

<script>
    // 选择所有p标签
    d3.selectAll('p').classed('sky',true)
  	// 选择所有奇数项的p标签
    d3.selectAll('p:nth-child(2)').classed('sky',true)
    // 选择所有奇数项的p标签
  	d3.selectAll('p:nth-child(even)').classed('sky',true)
</script>

D3根据条件选择:

  • filter((ele,ind)=>{}) 可基于节点元素或节点的索引进行过滤
  • filter(':nth-child(n)') 基于元素的索引位置过滤
<script>
    const n=[1,2,3]
    d3.selectAll('p')
      .filter((ele,ind)=>n.includes(ind))
      .classed('sky',true)
</script>

样式

D3给我们提供了三大类修改样式的方式,分别是:

  • style()方法,直接修改内联样式,不建议使用,因为有可能导致全局样式发生变化
  • classed()方法,增加或删除class,从而实现样式变化。
  • attr()方法,增加属性(会覆盖原来的属性),可以修改class、id、内联样式、自定义属性等,最万能。

下面我们来一个一个的来看下这三种方式。

<script>
		/*选择标题元素*/
    const title=d3.select('#title');

    /*style() 方法设置标题颜色*/
    // title.style('color','#00acec')

    /*attr() 方法通过对style 属性的设置,改变标题颜色*/
    // title.attr('style','color:#000')

    /*classed(class,true|false) 方法增删标题的class*/
    // title.classed('sky',true)

    /*attr() 方法设置class属性*/
    // title.attr('class','sky')

    /*用style的回调函数,让所有p元素偶数行变蓝*/
    /*d3.selectAll('p')
        .style('color',(ele,ind)=>ind%2?'#00acec':'#00f')*/

    /*用data数据绑定,动态设置p元素字体大小*/
    const data=[12,16,18,24,56]
    d3.selectAll('p')
        .data(data)
        .style('font-size',(ele,ind)=>{
            console.log(ele);
            return `${ele}px`
        })
</script>

属性

D3提供了两种修改DOM属性的方法:

  • attr()设置普通的内置属性和自定义属性
  • property()设置特殊的内置属性(如value、checked等)

通过下面的实例我们可以了解到如何使用D3修改DOM元素,首先有一段文本,label标签里main有三个checkbox类型的答案。首先通过selectAll选择所有label标签,并添加class样式。

然后基于预设的answer,选择相应的答案:

  1. 通过selectAll方法,选择所有input元素;
  2. 通过filter方法,筛选各个元素索引在answer数组那的元素;
  3. 通过property方法,设置上述筛选出来元素的checked属性为true。
<div class="question">
    <p class="title">《沁园春·长沙》里的诗句有哪些?</p>
    <label>
        <input type="checkbox"> 独立寒秋,湘江北去,橘子洲头。
    </label>
    <label>
        <input type="checkbox"> 看万山红遍,层林尽染;
    </label>
    <label>
        <input type="checkbox"> 好好学习,天天向上。
    </label>
</div>
<script>
    d3.selectAll('label').attr('class','opt')
    const answer=[0,1]
    d3.selectAll('input')
        .filter((ele,ind)=>answer.includes(ind))
        .property('checked',true)
</script>

内容

设置内容相对比较简单,可以直接在选取的对象那设置文本内容或者html内容,注意:会覆盖原本内容。

  • text()方法,设置文本内容
  • html()方法,设置html内容
<script>
    // 选择标题元素
    const title=d3.select('#title');
    // 选择内容元素
    const cont=d3.select('#cont');
    // 设置标题内容
    const titleText='沁园春·长沙';
    // 设置标签文本
    const p=`
        <p>独立寒秋,湘江北去,橘子洲头。</p>
        <p>看万山红遍,层林尽染;漫江碧透,百舸争流。</p>
        <p>鹰击长空,鱼翔浅底,万类霜天竞自由。</p>
        <p>怅寥廓,问苍茫大地,谁主沉浮?</p>
        <p>携来百侣曾游。忆往昔峥嵘岁月稠。</p>
        <p>恰同学少年,风华正茂;书生意气,挥斥方遒。</p>
        <p>指点江山,激扬文字,粪土当年万户侯。</p>
        <p>曾记否,到中流击水,浪遏飞舟?</p>
    `;

    // 填充文本内容
    title.text(titleText)
		// 填充html内容
    cont.html(p)


</script>

修改元素

添加DOM元素

接下来看到的各种语法依旧感觉似曾相识,就像是在用原生语法一样,这个D3好用的一点:

  • append() 方法,在当权选择的对象的最后追加制定名称的子元素
  • insert() 方法,用来前置元素,在某个元素前追加前置元素
  • data()join() 方法,以完全覆盖的形式,添加元素
  • data()enter()append() 方法,以插值的形式添加元素

下面我们还是在实例中看看D3是如何以上述四种方式添加DOM元素的。

<h1 id="title">沁园春·长沙</h1>
<article id="cont"></article>
<script>
    const arr=[
        '作者:毛主席',
        '独立寒秋,湘江北去,橘子洲头。',
        '看万山红遍,层林尽染;漫江碧透,百舸争流。',
        '鹰击长空,鱼翔浅底,万类霜天竞自由。',
        '怅寥廓,问苍茫大地,谁主沉浮?',
        '携来百侣曾游。忆往昔峥嵘岁月稠。',
        '恰同学少年,风华正茂;书生意气,挥斥方遒。',
        '指点江山,激扬文字,粪土当年万户侯。',
        '曾记否,到中流击水,浪遏飞舟?'
    ]
    // 选择内容元素集合
    const cont=d3.select('#cont');

    /* 一、append(),向cont中添加p元素
    * text()设置元素文本内容
    * */
    cont.append('p')
        .text('独立寒秋,湘江北去,橘子洲头。———— 未使用arr数组填充的数据')
        .attr('id','p1')

    /* 二、insert(要添加的元素,在哪个元素之前添加)
    * 在第一句的前面添加作者
    * */
    cont.insert('p','#p1')
        .text('作者:毛主席')

    /* 三、selectAll() 选择所有的p元素
    * data() 绑定数据
    * join() 基于数据添加p元素
    * text(d=>d) 设置DOM元素内容为数据内容
    * */
    /*cont.selectAll('p')
        .data(arr)
        .join('p')
        .text(d=>d)*/

    /*四、selectAll() 选择所有的p元素
    * data() 绑定数据
    * enter() 将数据和已有DOM元素做插值运算
    * append() 基于插值结果添加p元素
    * text(d=>d) 设置DOM元素内容为数据内容
    * */
    cont.selectAll('p')
        .data(arr)
        .enter()
        .append('p')
        .text(d=>d)

</script>

删除元素

删除就相对来说比较简单,使用remove() 方法就可以删除所有选择上的元素集合:

<script>
	d3.selectAll("p").remove()
</script>