最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Use d3.dispatch to broadcast events between d3 components - Stack Overflow

programmeradmin7浏览0评论

I'm currently working on creating a library of reusable d3 ponents and want to create an "interactive layer" ponent that basically creates a svg rectangle over my visualization and then broadcasts interactions with it to other ponents listening for those interactions ie. mouseover, mousemove, click, etc.

I've been following Mike Bostock's pattern for reusable d3 charts (/) and while I can create a dispatcher inside of each ponent to dispatch events internally I haven't been able to be successful in making separate ponents listen for another ponent's dispatch events. So basically I want one dispatcher for my library that all ponents then listen to if used.

I've done a lot of searching but haven't found an answer or even a hint as to how to do this, if anyone has any ideas it would be greatly appreciated.

I'm currently working on creating a library of reusable d3 ponents and want to create an "interactive layer" ponent that basically creates a svg rectangle over my visualization and then broadcasts interactions with it to other ponents listening for those interactions ie. mouseover, mousemove, click, etc.

I've been following Mike Bostock's pattern for reusable d3 charts (http://bost.ocks/mike/chart/) and while I can create a dispatcher inside of each ponent to dispatch events internally I haven't been able to be successful in making separate ponents listen for another ponent's dispatch events. So basically I want one dispatcher for my library that all ponents then listen to if used.

I've done a lot of searching but haven't found an answer or even a hint as to how to do this, if anyone has any ideas it would be greatly appreciated.

Share Improve this question asked Jun 6, 2014 at 13:20 briboubribou 7926 silver badges9 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 6

Here's an approach that associates the events directly with the d3 ponents instead of forcing you to register every event in your global namespace.

The ponent that broadcasts events might look like this:

namespace.interactiveLayer = function() {

    dispatch = d3.dispatch('myEvent'); 

    me = function() {
        // ...
        svg.on('click', function() {
          pos = d3.mouse(this);
          dispatch.myEvent(pos);
        });
    };

    me = d3.rebind(me, dispatch, 'on')

    return me;
};

The ponent that listens for events might look like this:

namespace.listenerLayer = function() {

    me = function() {
        interactive = namespace.interactiveLayer();
        d3.select("body").call(interactive);
        interactive.on('myEvent', function(d) {
            alert('You clicked on ' + d);
        });
    };

    return me;
};

I've put up a fleshed-out working example here .

So I realized I could achieve this by just saving a reference to d3.dispatch under my libraries namespace and using this inside of my ponents to tie into each others events.

Like this.

namespace = {}
namespace.dispatch = d3.dispatch('mousemove');

//Interactive layer ponent
namespace.dispatch.mousemove(args)

//Other ponents listening for events
namespace.dispatch.on('mousemove', function(args){
    //do something here
})

Then inside my interactive layer ponent I dispatch the event and in another ponent I listen for it.

I faced a similar problem, and solved it using the below design principles:

  1. Don't have the ponents talk to each other directly
  2. Keep a centralized orchestrator who is responsible for creating and managing ponent states
  3. Each ponent provides a contract of all the dispatch calls that it can make
  4. The orchestrator, while creating the ponents, also creates the necessary dispatch, and sets it during the instantiation of the ponent.
  5. The orchestrator listens to the events, and take appropriate actions as necessary (by invoking the public functions of other ponents)

Here's an example. This dummy "brush" ponent does not produce output, but you can see what's going on in the console log.

// Here's my re-usable ponent
var myown = {};
myown.brush = function(){

  // setup private variables
  var height, dispatcher;

  function my(group){
    group.each(function(d,i){
      // here is the code for creating brush
      // note `this` is also available (in addition to d and i), for e.g.
      var b = d3.select(this);

      console.log("inside my each "+height);
      if (dispatcher) {
        dispatcher.call("test1");
      }
    });
  }

  my.move = function (group, position){
    console.log("in move function");

    group.each(function(d,i){
      // here is the code for moving the brush
      // note `this` is also available (in addition to d and i), for e.g.
      var b = d3.select(this), that = this;

      console.log("inside move each");
      console.log(typeof(position));   // note it is function

      // now call that function
      // using "apply" and "arguments"
      let pos = position.apply(that, arguments);
      console.log(pos);
      if (dispatcher) {
        dispatcher.call("test2");
      }
    })

    return my;
  }

  my.height = function(value){
    if(!arguments.length) return value;
    height = +value;
    return my;
  }

  my.dispatcher = function(value){
    if(!arguments.length) return dispatch ? true : false;
    dispatcher = value;
    return my;
  }

  return my;
};

// the orchestrator creates the dispatch, and the callers
var myDispatch = d3.dispatch("test1", "test2");

myDispatch.on("test1", function(){
  console.log("test1 has been called!!!");
  // call the public functions of other ponents (for e.g. similar to brush.move) here
});

myDispatch.on("test2", function(){
  console.log("and now, test2 has also been called!!!");
  // call the public functions of other ponents (for e.g. similar to brush.move) here
});

// Okay, now use all that nice code

var data = [
  {id: 1, mv: [100,500]},
  {id: 2, mv: [300,600]},
  {id: 3, mv: [800,1000]}
];

// orchestrator sets the dispatch created during instantiantion
var brush = myown.brush().height(90).dispatcher(myDispatch);

var svg = d3.select("svg");

var g = svg.selectAll("g").data(data).enter()
  .append("g")
  .attr("transform", (d,i)=>"translate(0,"+i*100+")");

g.call(brush).call(brush.move, (d,i)=>d.mv);
<script src="https://cdnjs.cloudflare./ajax/libs/d3/5.7.0/d3.min.js"></script>
<title>Boilerplate for chaining ponent, and also for testing dispatch events</title>

<svg width=1500 height=1000></svg>

Note that if you do not set a dispatch during instantiation, i.e.

var brush = myown.brush().height(90); //.dispatcher(myDispatch);

the ponent would simply not make the calls, as a dispacher has not been set.

发布评论

评论列表(0)

  1. 暂无评论