d3.select (this) работает при наведении курсора, но не при вызове функции при наведении курсора - PullRequest
0 голосов
/ 03 мая 2018

Я новичок в javascript и в настоящее время пытаюсь выбрать объект this , пытаясь сделать выбор d3. Я сделал следующий пример с вызываемой функцией и событием on mousemove:

function changeFont() {
  d3.select(this)
    .attr('font-size', '2em')
}

...
.on('mousemove', function() {
  var mouse = d3.mouse(this);
  var xVal = mouse[0];

  // this would work, but not when its called in a function
  // d3.select(this)
  //  .attr('font-size', '2em')

  // this works
  d3.select(this)
   .attr("opacity", 1)

  // this doesnt
  changeFont()
});

В моем основном сценарии, не показанном здесь, я организую свой код, написав функции, которые обрабатывают эффекты перемещения мыши, наведения мыши и т. Д. Однако из-за этих функций я сталкиваюсь с этой проблемой, когда я не могу сделать d3.select (this) внутри этой функции наведения мыши ... Есть мысли о том, что я должен делать по-другому?

Должен ли я передать this в качестве параметра моей функции changeFont ()? Или я должен получить доступ к этому другим способом?

Спасибо!

Ответы [ 3 ]

0 голосов
/ 03 мая 2018

Хотя ответ Эндрю может быть наиболее подходящим, если вы понимаете вопрос буквально, я хотел бы добавить к нему два моих цента. Ваша настоящая проблема заключается не в том, чтобы овладеть this, а в том, чтобы повторно получить доступ к этому элементу, чтобы применить ваши манипуляции. Так как возиться с this может быть проблемой в JavaScript, возможно, стоило бы использовать немного другой подход, вместо этого непосредственно передавая выделение. Это также улучшит производительность, поскольку нет необходимости повторно выбирать this снова и снова.

Во-первых, давайте немного реорганизовать вашу функцию changeFont(), чтобы принять объект выделения.

function changeFont(selection) {
  selection
    .attr('font-size', '2em');
}

Обратите внимание, как это делает функцию более применимой в целом, поскольку она не делает никаких предположений относительно переданного ей выбора. Это может быть d3.select(this), выделение, содержащее несколько элементов, или любой другой объект выделения D3. Кроме того, вам не нужно сохранять предыдущую область действия this.

Существует два основных способа вызова этой функции.

  1. Очевидный передаст выделение в качестве аргумента при вызове функции:

    const d3This = d3.select(this);
    changeFont(d3This);
    
  2. К счастью, есть более элегантный способ сделать это, прибегнув к собственному D3 selection.call(), который даже позволяет объединять методы, если вам нужно сделать несколько вызовов для одного выбора.

    function changeFont(selection) { selection.attr("font-size", "2em"); }
    function changeFill(selection) { selection.attr("fill", "limegreen"); }
    function changeOpacity(selection) { selection.attr("opacity", "0.1"); }
    
    // ...
    .on("mouseover", function() {
      // Call the functions for this element.
      d3.select(this)
        .call(changeFont)
        .call(changeFill)
        .call(changeOpacity);
    
      // Instead, you could also apply the same pattern to all texts.
      d3.selectAll("text")
        .call(changeFont)
        .call(changeFill)
        .call(changeOpacity);
    
    }
    
0 голосов
/ 04 мая 2018

Просто для полноты, поскольку на этот вопрос уже есть два очень хороших ответа: вы можете избежать путаницы с this, если будете использовать третий и второй аргументы вместе. Это то, что даже разработчики D3 в конце концов забывают.

В нескольких методах D3 текущий элемент DOM является просто текущим индексом группы узлов. Итак, в анонимной функции ...

.on("mouseover", function(_, i, n) {

... this это просто n[i], который вы можете просто передать другим функциям. _ здесь соответствует первому аргументу, данным: я использую _ только для того, чтобы следовать соглашению, которое показывает, что этот аргумент не используется.

Хорошая особенность этого подхода в том, что вы даже можете (по любым причинам) использовать функции стрелок:

d3.select("body").selectAll(null)
  .data(["foo", "bar", "baz"])
  .enter()
  .append("p")
  .text(String)
  .on("mouseover", (_, i, n) => {
    changeFont(n[i])
  });

function changeFont(element) {
  d3.select(element).style("font-size", "2em")
}
<script src="https://d3js.org/d3.v5.min.js"></script>

Конечно, вы не можете получить элемент DOM, используя this внутри функции стрелки.

0 голосов
/ 03 мая 2018

Давайте посмотрим, что this определяется как для каждого из ваших подходов:

// console.log(this) in inline function:
<svg width="960" height="960"> 

// console.log(this) in function called from inline function:
Window → file:///fileName.html

this устанавливается в зависимости от того, как вызывается функция. D3 удобно устанавливает this как элемент DOM, которым манипулируют, используя .apply в функции, переданной в selection.attr(), selection.on() и т. Д. Однако он не делает этого для функций, вызываемых в функции, переданной в selection.attr(), selection.on() и т. Д.

Мы можем видеть, что this действительно является элементом DOM, если мы регистрируем this в функции, переданной selection.on(). Если this явно не установлено, это будет окно (если только не используется строгий режим, то оно будет неопределенным). Мы можем видеть во вложенной функции, this действительно окно.

ответ Altocumulus и ответ Герардо полностью исключают проблему this, кроме того, вы также можете передать this в качестве некоторого регулярного аргумента функции (этот шаблон наблюдается в некоторых Примеры). Но если вы хотите просто иметь возможность копировать и вставлять код из встроенной функции в какую-то отдельно определенную функцию, вы можете использовать apply, которая сохранит this в качестве изменяемого элемента:

d3.select("body")
  .append("svg")
  .attr("width", 960)
  .attr("height", 960)
  .on('mousemove', function() {
     var mouse = d3.mouse(this);
     console.log("inline: ", mouse[0]);
     someFunction.apply(this);
});

function someFunction() {
  var mouse = d3.mouse(this);
  console.log("not inline: ", mouse[0]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Если вам нужно было передать параметры вашей функции вместе с этим, вы все равно можете использовать apply:

someFunction.apply(this,[parameterA,parameterB,...]);
function someFunction(parameterA,parameterB) { }

d3.select("body")
  .append("svg")
  .attr("width", 960)
  .attr("height", 960)
  .on('mousemove', function() {
     var mouse = d3.mouse(this);
     console.log("inline: ", mouse[0]);
     someFunction.apply(this,[mouse[0],mouse[1]]);
});

function someFunction(x,y) {
  var mouse = d3.mouse(this);
  console.log("not inline: ", mouse[0],x,y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Однако эта встроенная функция, вызывающая другие функции, может быть просто дополнительной работой. Если вы только вызываете функцию во встроенной функции, то просто передайте вызываемую функцию непосредственно в selection.on(), это сохранит this без каких-либо дополнительных шагов, так как d3 применит к нему ожидаемое значение (это также по-прежнему дает доступ к данным и индексу, если необходимо):

d3.select("body")
  .append("svg")
  .attr("width", 960)
  .attr("height", 960)
  .on('mousemove', someFunction)

function someFunction() {
  var mouse = d3.mouse(this);
  console.log(mouse[0]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

В этом случае не ставьте скобки на функцию, мы не хотим возвращать результат функции, мы хотим использовать саму функцию.


Я использую apply (Function.prototype.apply()) в моих примерах, но вы также можете использовать call (Function.prototype.call()), как отмечает Altocumulus ниже . Использование вызова довольно похоже. Если вы не передаете в функцию какие-либо параметры и хотите сохранить только this, использование будет таким же: someFunction.apply(this) / someFunction.call(this). Но, если передаются параметры, call не использует массив для параметров:

d3.select("body")
  .append("svg")
  .attr("width", 960)
  .attr("height", 960)
  .on('mousemove', function() {
     var mouse = d3.mouse(this);
     someFunction.call(this,mouse[0],mouse[1]); // first parameter will be `this` for someFunction, rest of parameters follow
});

function someFunction(x,y) {
  var mouse = d3.mouse(this);
  console.log(mouse[0],x,y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
...