pool append remove operations?

31 views
Skip to first unread message

Ivan L

unread,
Apr 4, 2015, 11:58:20 AM4/4/15
to d3...@googlegroups.com
In the general update pattern example you append and remove nodes of the same type. Given that the actual dom node is behind the api is there some way to instruct d3 to pool the node instances so node count doesnt unnecessarily increase?

gabriel

unread,
Apr 6, 2015, 10:18:10 AM4/6/15
to d3...@googlegroups.com
Hi Ivan, 

On Saturday, April 4, 2015 at 4:58:20 PM UTC+1, Ivan L wrote:
In the general update pattern example you append and remove nodes of the same type.  Given that the actual dom node is behind the api is there some way to instruct d3 to pool the node instances so node count doesnt unnecessarily increase?


Unless you use a key function (by which you can help D3 determine which of the already present DOM elements should be used for which data items) on your data bind operation, the elements you previously had in the DOM will be reused (the first for the first, the second for the second, etc).
It is not that all the elements are scraped and recreated from zero.

If do you want to have an object pool solution (because your initialization is for some reason super expensive) then it might be useful to know that `append` and  `remove` are very thin wrappers around `select` and `each` respectively.

If you pass a function to `selection.select`, whatever you return will be what's selected next.  So, on an `enter` selection, if you `.select(acquire)` where acquire is a funciton that gets the item from a pool (or creates it when needed) and attaches it to the parent (which will be the `this` in your function) then you can easily implement a pooling solution.

Here's a very schematic implementation:
[ Note that you could continue operating on what's returned from .select(acquire) ]


    var pool = []
      , timeoutId = setInterval(update, 750)
      , random = d3.random.normal(20, 10)
      , format = d3.time.format('%X')

    update()

    function update() {
      var data = d3.range.apply(null, [random(), random()].sort(d3.ascending))
        , update = d3.select('ul').selectAll('li').data(data)
        , enter = update.enter().select(acquire)
        , exit = update.exit().each(release)
    }

    function acquire(d, i) {
      var item = pool.pop() 

      if (!item) {
        item = document.createElement('li')
        item.innerText = 'created: ' + format(new Date())
      }

      this.appendChild(item)
      return item
    }

    function release(d, i) {
      var item = this
      item.parentNode.removeChild(item)
      pool.push(item)
    }


HTH,
Gabriel






Ivan L

unread,
Apr 20, 2015, 11:51:07 AM4/20/15
to d3...@googlegroups.com
This works extremely well (at least this test fragment).  I'm still a little surprised something like this isn't included in the core itself.   Just leaving the fragment on for a few minutes the memory and dom usage is a complete flatline making it resource usage comparable to a virtual dom framework like react.

Very cool.
Reply all
Reply to author
Forward
0 new messages