d3.select(this)适用于鼠标悬停,但不适用于鼠标悬停中调用的函数

时间:2018-05-03 17:54:04

标签: javascript d3.js this

我是javascript的新手,目前正在努力选择这个对象,同时尝试进行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()
});

在我未在此处显示的主脚本中,我通过编写处理每个mousemove,mouseover等效果的函数来组织我的代码。但是由于这些功能,我遇到了这个问题,我无法在那个鼠标悬停功能中执行 d3.select(this) ...对于我应该采取哪些不同的做法?

我应该将 this 作为参数传递给我的changeFont()函数吗?或者我应该以不同的方式访问 this 吗?

谢谢!

3 个答案:

答案 0 :(得分:9)

虽然安德鲁的answer可能是最合适的,如果你从字面上理解问题,我想加上我的两分钱。你真正的问题似乎不是要抓住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()来实现它,如果你需要在同一个选择上进行多次调用,它甚至允许方法链接。 / p>

    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);
    
    }
    

答案 1 :(得分:6)

让我们看看您的每种方法都定义了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.apply等的函数使用selection.attr(),方便地将selection.on()设置为被操纵的DOM元素。但是,它不会这样做对于传递给selection.attr()selection.on()等的函数内调用的函数

如果我们在传递给this的函数中记录this,我们可以看到selection.on()确实是DOM元素。如果未明确设置this,则它将是窗口(除非使用严格模式,否则它将是未定义的)。我们可以在嵌套函数中看到,this确实是窗口。

Altocumulus的answer和Gerardo的answer完全避免了this问题,此外你也可以将this作为常规参数传递给功能(在某些例子中可以看到这种模式)。但是,如果您只想将内联函数中的代码复制并粘贴到某个单独定义的函数中,则可以使用apply,这将保留this作为要修改的元素:

&#13;
&#13;
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]);
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;

如果您需要将参数传递给您的函数,您仍然可以使用apply:

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

&#13;
&#13;
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);
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;

但是,调用其他函数的内联函数可能只是额外的工作。如果您在内联函数中调用函数,那么只需将被调用的函数直接传递给selection.on(),这将保留this而无需任何额外步骤,因为d3将应用预期它的值(如果需要,它还可以让你访问数据和索引):

&#13;
&#13;
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]);
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;

在这种情况下不要将括号放在函数上,我们不想返回函数的结果,我们想要使用函数本身。

我在我的示例中使用了apply(Function.prototype.apply()),但您也可以使用call(Function.prototype.call()),正如Altocumulus注意到below。通话的使用非常相似。如果您未将任何参数传递给该函数,并且只想保留this,则使用情况相同:someFunction.apply(this) / someFunction.call(this)。但是,如果传递参数,则调用不使用数组作为参数:

&#13;
&#13;
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);
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;
&#13;
&#13;

答案 2 :(得分:3)

为了完整起见,因为这个问题已经有两个非常好的答案:如果你使用第三个和第二个参数组合,你可以避免与this的混淆。这是D3开发人员最终忘记的事情。

在几种D3方法中,当前的DOM元素只是节点的当前索引。组。所以,在匿名函数中......

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

... this只是n[i],您可以将其传递给其他功能。这里的_对应于第一个参数,即数据:我使用_只是为了遵循显示未使用此参数的约定。

这种方法的好处在于你甚至可以(出于任何原因)使用箭头功能:

&#13;
&#13;
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")
}
&#13;
<script src="https://d3js.org/d3.v5.min.js"></script>
&#13;
&#13;
&#13;

当然,您无法在箭头功能中使用this获取DOM元素。