zoukankan      html  css  js  c++  java
  • D3.js从源码分析到精通(三)

    data

     let fruits = [
          {name: "orange", value: 200},
          {name: "apple", value: 124},
          {name: "banana", value: 32}
        ];
    
        let d = d3.select('#bbb').selectAll('div')
          .data(fruits)
          .enter().append('div');
    	d.style('color', function(d) {
                         if (d % 2 === 0) {
                             return "green";
                         } else {
                             return "red";
                         }
                     })
          d.text(function(d) { return d.name + ": " + d.value; });
    // enter() 是输出的意思
    

    d3.select('#bbb')

    选择一个节点

    .selectAll('span')

    没有这个元素,返回一个空数组

    .data(data)

    有三个元素,会循环三次

    enter()

    将为5个元素中的每个元素创建一个跨度

    .append('span')

    添加到body元素上

    text()

    打印出来

    我们知道text在函数中

    .text(function (d, i,groups) {
                     console.log("d: " + d); // 每一样
                     console.log("i: " + i); // 索引
                     console.log("this: " + this);// 当前dom的引用
    					groups // 全部引用的dom
                     return d;
                 });
    
    .data(data,function(v,i,g){
         // 每一项, 索引,所有数据,绑定的全部#bbb节点
         console.log(v, i, g,this);
         return i
       })
    

    通过查询大量资料,我们了解了如果提供了第二个参数(成为键函数),告诉d3的插入位置

    无论键功能是否存在,数据都将是对象,键功能不会更改基础数据

    我们会发现data实际存在在每一个dom的__data__ 属性上

    join()

    上一个案例

    join里面有三个参数函数

    分别是 enter,update,exit

    d3.select('.aaa').selectAll('div').data([1,2,3,4])
          // .join('p').text(v=>v)
          .join(v=>v.append('p')).text(v=>v)
    两者的效果类似
    

    enter()

    有点理解输入的用法了

    <div id="bbb">
    <div class="aaa"></div>
    <div class="aaa"></div>
    <div class="aaa"></div>
    </div>
    
    d3.select('#bbb').selectAll(".aaa")
       .data([1,2,3,5,6]).text(v=> {
       return v;
     }).enter().append('h1').text(v=>v)
    
    结果是前三个 .aaa
    后面两个是  h1
    ============
    // 有元素会直接更新上去
    <div class="aaa">
      <div class="bbb"></div>
      <div class="bbb"></div>
      <div class="bbb"></div>
      <div class="bbb"></div>
    </div>
    d3.select('.aaa').selectAll('.bbb').data([1,2])
          .text(v=>v)
    // 如果多了可以再添加其他
     d3.select('.aaa').selectAll('.bbb').data([1,2,4,5,67,7])
          .text(v=>v).enter().append('h1').text(v=>v)
    

    exit()

    删除多余的元素

    <div class="aaa">
      <div class="bbb"></div>
      <div class="bbb"></div>
      <div class="bbb"></div>
      <div class="bbb"></div>
    </div>
    
       d3.select('.aaa').selectAll('.bbb').data([1,2])
          .text(v=>v).exit().remove()
    结果为
    <div class="aaa">
      <div class="bbb">1</div>
      <div class="bbb">2</div>
    </div>
    

    datum

    data() 添加元素组

    datum() 分配给各个元素

    document.body.__data__ = 42;
    类似
    d3.select("body").datum(42);
    

    源码

    export default function(value) {
      return arguments.length
          ? this.property("__data__", value)
          : this.node().__data__;
    }
    

    实践中

       let a = d3.select('.aaa').selectAll('div').append('p').datum(10).text(v => v);
        // 查询这个值
    	console.log(a.datum());
        // 10
        // 我们会发现修改了这个值
        a.datum(12).text(v=>v)
    
    <ul id="list">
      <li data-username="shawnbot">Shawn Allen</li>
      <li data-username="mbostock">Mike Bostock</li>
    </ul>
    
     d3.select('#list').selectAll('li').datum(function(){
          // 可以拿到自定义属性
          return (this as HTMLElement).dataset
        }).text(v=>v.username)
    

    on()

    类似于addEventListener()

    d3.select('.input').on('change', function(e) {
          console.log(e);
        });
    

    源码分析

    function contextListener(listener) {
      return function(event) {
        listener.call(this, event, this.__data__);
      };
    }
    

    我们会发现数据会传给函数的第二个参数,写一个案例

      d3.select('button').datum(333).on('click',function(e,data){
                console.log(e);
                console.log(data);// 第二个参数拿到了数据 333
            })
    

    当只写入一个参数,我们会发现可以拿到返回的值或者函数

    aaa.on('clicks', function (d) {
                    return 3
                })
            console.log(aaa.on('clicks')());// 3
    ===
         aaa.on('clicks', 3)
         console.log(aaa.on('clicks'));// 3
    

    源码分析

    function parseTypenames(typenames) {
      return typenames.trim().split(/^|s+/).map(function(t) {
        var name = "", i = t.indexOf(".");
        if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
        return {type: t, name: name};
      });
    }
    console.log(parseTypenames$1('aaa.bbb ccc.ddd'));
    // [ { type: 'aaa', name: 'bbb' }, { type: 'ccc', name: 'ddd' } ]
    

    从中我们可以分析到可以多个鼠标事件作用于一个函数

    我们会发现鼠标进入和点击同时作用于这个函数
    let aaa=d3.select('.aaa');
     aaa.on('click mouseenter', function(){
                console.log(1);
            })
    其中我感觉name类似于标识的意思
     aaa.on('click.name1 mouseenter.name2', function(){
                console.log(1);
            })
    

    在源码中,我们得到了一些分析

    aaa.on('clicks')
    由于没有第二个参数,源码中会通过 removeEventListener 删除这个事件
    let aaa=d3.select('.aaa');
            aaa.on('click.name1 mouseenter.name2', function(){
                console.log(1);
            })
            aaa.on('click.name1',function(){
                console.log(2);
            })
    如果出现这个点击事件重复的情况下
    我们会发现点击的时候一直打印的是 2
    源码中通过removeEventListener删除原有的,然后addEventListener 进行添加
    如果使用的selectAll 源码中也会通过 this.eath 进行多个添加事件
    

    合成事件

    创建和分派DOM事件 通常称为合成事件,而不是浏览器本身出发的事件

    customEvent

    event= new VustomEvent(typeArg,customEventInit)
    typeArg  事件名称
    customEventInit 可选
       里面的字段有
       datail 默认null
       可以添加一个对象进行分配
       这里面其实有个属性
        cancelable: true   是否被清除
    

    demo

    <form action="">
      <input type="text" class="input">
    </form>
    
     let form = document.querySelector('form')
        let input = document.querySelector('input')
    
    // 创建一个自定义时间,通过冒泡进行传递
        const eventAwesome = new CustomEvent('awesome', {
          bubbles: true,
          detail: { text: ()=>input.value }
        });
    
    // 父元素接受子元素传递的值
        form.addEventListener('awesome', e => console.log(e.detail.text()));
    
    // 子元素监听值,传递给父亲, 自己写的时候容易出错的是
        input.addEventListener('input', e => e.target.dispatchEvent(eventAwesome));
    

    event.preventDefault()

    默认行为,这个 cancelable: true 一起使用,一直纠结这个属性有什么用,查阅了大量知道,写一个简单的案例

    我们会发现调用 event.preventDefault() 指令取消这些操作, 对dom.dispatchEvent(event) 的调用为false

      let event = new CustomEvent('aaa',
          // 通过时间冒泡,父元素拿到子元素的数据
          {
            bubbles: true, cancelable: true,
            detail: {text: 333}
          });
        document.querySelector('.input').addEventListener('change', function() {
          console.log(this.dispatchEvent(event));  // false
        })
        document.querySelector('form').addEventListener('aaa', e => {
          e.preventDefault();
        });
    

    结论: cancelable: true的时候event.preventDefault()才能使用

    dispatch

    上面的事件合成是为了派发任务的原生

    功能,点击按钮调用原生自带的 mouseenter 事件随机修改颜色
    由于我喜欢用angular这里就用angular 代码了
    
    <div class="aaa"></div>
    <button (click)="clickMount()">Click</button>
    
    export class HelloComponent implements OnInit, AfterViewInit {
    
      randomHexColorCode() {
        let n = (Math.random() * 0xfffff * 1000000).toString(16);
        return '#' + n.slice(0, 6);
      };
    
      ngAfterViewInit() {
        let that = this;
        d3.select('.aaa').on('mouseenter', function(e) {
          (this as HTMLElement).style.background = that.randomHexColorCode();
        });
      }
    
      clickMount() {
        d3.select('.aaa').dispatch('mouseenter');
      }
    }
    

    我们从源码的基础上去简化demo

    <div className="bbb">
                    <div className="aaa">
                    </div>
     </div>
    
     	    let aaa=d3.select('.aaa');
            aaa.on('click',function(e){
                aaa.dispatch('clicks',{
                    bubbles:true,
                    cancelable:true,
                    detail:{
                        test:12
                    }
                })
            })
    	   // 通过冒泡,传给父元素
            d3.select('.bbb').on('clicks', function (e) {
                e.preventDefault();
                console.log(e.detail);
            });
    
    dispatch 第二个参数可以是函数的形式
    aaa.dispatch('clicks',function(){
                    return {
                        bubbles:true,
                        detail:{
                            text:'xxxx'
                        }
                    }
                })
    
  • 相关阅读:
    JavaScript 消息框
    DOM事件
    修改输入框placeholder文字默认颜色-webkit-input-placeholder
    css—文字渐变色
    css—各浏览器下的背景色渐变
    $.ajax()方法详解
    使用meta实现页面的定时刷新或跳转
    python的连接mysql的安装
    django安装
    速查
  • 原文地址:https://www.cnblogs.com/fangdongdemao/p/13724537.html
Copyright © 2011-2022 走看看