d3.selection type check in IE

前端 未结 1 1110
再見小時候
再見小時候 2021-01-21 08:10

How to check if a given object is a d3 selection?

The following code prints true in Chrome and Firefox, but false in Internet Expl

相关标签:
1条回答
  • 2021-01-21 08:27

    Update 2017-01-17

    With the release of D3 v4 this problem has vanished (changelog):

    Selections no longer subclass Array using prototype chain injection; they are now plain objects, improving performance.

    And the API docs explicitly state:

    # d3.selection() <>

    […] This function can also be used to test for selections (instanceof d3.selection)

    Using the new version, the following code will actually evaluate to true in all browsers:

    d3.select() instanceof d3.selection   // true in Chrome, FF, IE
    

    For all those still on v3 the original answer below has an analysis and a workaround for the problem.


    Problem

    Due to the inner workings of D3 every browser which supports Object.prototype.__proto__ will print true, whereas browsers lacking support for __proto__ will print false. Checking the compatibility list it's obvious, that IE<11 will evaluate the expression to false. For this reason, you won't be able to use instanceof d3.selection to check for a D3 selection in IE<11. This is a known issue with D3, but it was closed and will not get fixed.

    Analysis

    From D3's github repository:

    1. selection/selection.js

    Looking at the definition of d3.select() which is the entry point of your call:

    d3.select = function(node) {
      // ... removed for brevity
    
      return d3_selection([group]);
    };
    

    This will eventually return the result of the call to d3_selection(), which in turn will subclass d3_selectionPrototype = d3.selection.prototype.

    function d3_selection(groups) {
      d3_subclass(groups, d3_selectionPrototype);
      return groups;
    }
    
    1. core/subclass.js

    Finally, the implementation of d3_subclass() provides the answer to the problem:

    var d3_subclass = {}.__proto__?
    
    // Until ECMAScript supports array subclassing, prototype injection works well.
    function(object, prototype) {
      object.__proto__ = prototype;
    }:
    
    // And if your browser doesn't support __proto__, we'll use direct extension.
    function(object, prototype) {
      for (var property in prototype) object[property] = prototype[property];
    };
    

    It checks, if the browser supports Object.prototype.__proto__ by checking for the existence of the __proto__ accessor property on an empty object {}. If the browser supports it, D3 will directly assign the prototype, thus making it an instance of d3.selection. Otherwise, all properties of the prototype will be copied over to the object to be returned without explicitely setting the prototype. In this case your expression will evaluate to false.

    Workaround

    Because d3.selection is provided as a means to extend the selection's functionality you could implement a workaround by adding a new property to d3.selection which will, as was explained above, be made accessible by any selection, wether by prototyping or by copying properties.

    // Include this at the start of your script to include the
    // property in any selection created afterwards.
    d3.selection.prototype.isD3Selection = true;
    
    console.log(d3.select(document.body).isD3Selection);   // true in any browser
    
    0 讨论(0)
提交回复
热议问题