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

Can*_*ice 9 javascript this d3.js

我是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()
});
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢!

alt*_*lus 11

虽然安德鲁的回答可能是最合适的,如果你从字面上理解这个问题,我想加上我的两分钱.你真正的问题似乎不是要抓住this,而是反复访问该元素以应用你的操作.由于摆弄thisJavaScript可能是一种痛苦,因此可能需要通过直接传递选择来采用稍微不同的方法.这也将提高性能,因为无需this一遍又一遍地重新选择.

首先,让我们稍微重构您的changeFont()函数以接受选择对象.

function changeFont(selection) {
  selection
    .attr('font-size', '2em');
}
Run Code Online (Sandbox Code Playgroud)

注意,这使得函数如何更普遍适用,因为它不会对传递给它的选择做出任何假设.它可能是您d3.select(this)的选择,包含多个元素或任何其他D3选择对象.此外,您不需要保留以前的this范围.

调用此函数基本上有两种方法.

  1. 在调用函数时,显而易见的将直接将选择作为参数传递:

    const d3This = d3.select(this);
    changeFont(d3This);
    
    Run Code Online (Sandbox Code Playgroud)
  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);
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 很好,这是一个有用的模式,我很惊讶我没有看到更多. (3认同)

Ger*_*ado 7

仅出于完整性考虑,因为此问题已经有两个非常好的答案:this如果将第三个和第二个参数组合使用,则可以避免混淆。这甚至是D3开发人员最终都会忘记的东西。

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

.on("mouseover", function(_, i, n) {
Run Code Online (Sandbox Code Playgroud)

... this只是n[i],您可以将其传递给其他功能。在_这里对应于第一个参数,基准:我使用的_只是遵循这一节目没有使用这种说法的惯例。

这种方法的好处是,您甚至可以(无论出于何种原因)使用箭头函数:

.on("mouseover", function(_, i, n) {
Run Code Online (Sandbox Code Playgroud)
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")
}
Run Code Online (Sandbox Code Playgroud)

当然,您不能this在arrow函数内部使用DOM元素。

  • 第三个论点很少使用,说实话,它甚至还没有在我的脑海中浮现-不错的答案。 (3认同)

And*_*eid 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
Run Code Online (Sandbox Code Playgroud)

this由函数的调用方式设置.D3方便设置this是通过使用被操纵的DOM元素.apply传递给函数selection.attr(),selection.on()等等.但是,它并没有一个名为传递给函数中的函数做到这一点selection.attr(),selection.on()等等.

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

Altocumulus的答案和Gerardo的答案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]);
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

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

someFunction.apply(this,[parameterA,parameterB,...]);
function someFunction(parameterA,parameterB) { }
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

但是,这个调用其他函数的内联函数可能只是额外的工作.如果你只是在内联函数中调用一个函数,那么只需将被调用的函数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]);
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

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


Function.prototype.apply()在我的例子中使用了apply(),但你也可以使用call(Function.prototype.call()),正如下面的 Altocumulus所说.通话的使用非常相似.如果您没有将任何参数传递给函数并且只想保留this,则用法是相同的:someFunction.apply(this)/ someFunction.call(this).但是,如果传递参数,则调用不会使用数组作为参数:

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);
}
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)