Расширение d3.selection в v5 - это правильный способ сделать это? - PullRequest
0 голосов
/ 11 октября 2018

Справочная информация: Я пытался решить проблему добавления существующих локальных SVG файлов в d3 SVG-контейнер в настольном приложении Electron.Я обнаружил, что не могу использовать d3.svg() для локальных файлов, потому что fetch не работает с протоколом file (так говорится в сообщении об ошибке).

Я сталкивался с этим gist ссылка на этот вопрос для расширения d3.selection, и он, кажется, делает именно то, что мне нужно, добавляя функции appendHTML и appendSVG.

Когда я тестировал код (хотя внизу), он выдавал ошибку: «Cannot read свойство 'prototype of undefined" - задыхаясь в этой строке:

d3.selection.enter.prototype.appendHTML 

Я копалсянемного через вход в консоль и придумал это изменение, и, кажется, оно работает:

d3.selection.prototype.enter.prototype.appendHTML   

Мой вопрос: Я правильно делаю?Что-то изменилось в d3, что требует дополнительной ссылки prototype?Я не являюсь Javascript или d3 hero и хотел бы понять, в чем здесь разница.

d3.selection.prototype.appendHTML =
    d3.selection.prototype.enter.prototype.appendHTML = function (HTMLString) {
        return this.select(function () {
            return this.appendChild(document.importNode(new DOMParser().parseFromString(HTMLString, 'text/html').body.childNodes[0], true));
        });
    };

Оригинальный код

 d3.selection.prototype.appendHTML =
    d3.selection.enter.prototype.appendHTML = function(HTMLString) {
        return this.select(function() {
            return this.appendChild(document.importNode(new DOMParser().parseFromString(HTMLString, 'text/html').body.childNodes[0], true));
        });
    };


 d3.selection.prototype.appendSVG =
    d3.selection.enter.prototype.appendSVG = function(SVGString) {
        return this.select(function() {
            return this.appendChild(document.importNode(new DOMParser()
            .parseFromString('<svg xmlns="http://www.w3.org/2000/svg">' + SVGString + '</svg>', 'application/xml').documentElement.firstChild, true));
        });
    };

1 Ответ

0 голосов
/ 11 октября 2018

Ответы, которые вы упомянули, используют D3 v3, но ситуация значительно изменилась с v3 до v4 / v5.Основное отличие, когда дело доходит до выборок, покрывается одним предложением в changelog :

Выборы больше не подклассируют Array с использованием инжекции цепочки прототипов;теперь они - простые объекты, улучшающие производительность.

Хотя это звучит довольно просто, тем не менее, потребовались значительные изменения под капотом.Все объекты выбора теперь являются экземплярами функции Selection, которая не предоставляется напрямую.d3.selection - это функция, возвращающая новый экземпляр Selection:

function selection() {
  return new Selection([[document.documentElement]], root);
}

Хотя оба Selection и d3.selection имеют один и тот же прототип , который содержит свойство .enter, нет свойства .enter, если не создан экземпляр, следовательно, ошибка в вашем коде.

Правильный способ расширения объектов выбора D3 в v4 / v5 будет следующим:следующие строки:

d3.selection    
  .prototype    // This prototype is shared across all types of selections.   
  .appendHTML = // Apply changes to the selection's prototype.

Поскольку свойство prototype Selection и d3.selection указывает на один и тот же объект, эти изменения будут влиять как на обычный, так и на выбор, поскольку оба они являются экземплярамиSelection функция.

Как вы можете видеть, это всего лишь первая строка вашего собственного кода, и это прекрасно.Ваше расширение использует d3.selection.prototype.enter.prototype.appendHTML только вид работ: оно не приносит ни вреда, ни пользы!Установка свойства для функции .enter не имеет смысла, так как никогда не создается экземпляр, созданный из этой функции.

Посмотрите на следующую рабочую демонстрацию, которую я взял из сущности, с которой вы связались в своем вопросе:

d3.selection.prototype.appendHTML =
  function(HTMLString) {
    return this.select(function() {
      return this.appendChild(
        document.importNode(
          new DOMParser().parseFromString(HTMLString, 'text/html').body.childNodes[0], true)
        );
    });
  };

d3.selection.prototype.appendSVG =
  function(SVGString) {
    return this.select(function() {
      return this.appendChild(
        document.importNode(
          new DOMParser()
            .parseFromString('<svg xmlns="http://www.w3.org/2000/svg">' + SVGString + '</svg>', 'application/xml').documentElement.firstChild, true));
    });
  };

d3.select('.container').appendHTML('<svg><g><rect width="50" height="50" /></g></svg>');

var svg = d3.select('.container')
  .appendHTML('<svg xmlns="http://www.w3.org/2000/svg"><g><circle class="circle1" cx="50" cy="50" r="50"></circle></g></svg>')
  .select('g');

svg.appendSVG('<circle class="circle2" cx="20" cy="20" r="20"></circle>');
svg.appendSVG('<rect width="30" height="30"></rect>');
div,
svg {
  border: 1px solid silver;
  margin: 10px;
}

rect {
  fill: skyblue;
}

.circle1 {
  fill: orange;
}

.circle2 {
  fill: lime;
}
<script src="https://d3js.org/d3.v5.js"></script>
<div class="container"></div>
...