Je suis nouveau sur javascript et je ne parviens pas à sélectionner l'objet this tout en essayant de faire une sélection d3. J'ai créé l'exemple suivant, avec une fonction que j'appelle et un événement 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()
});
Dans mon script principal non présenté ici, j'organise mon code en écrivant des fonctions qui gèrent chacun des effets mousemove, mouseover, etc. Cependant, à cause de ces fonctions, je rencontre ce problème où je ne peux pas faire d3.select (this) à l'intérieur de cette fonction mouseover ... Avez-vous une idée de ce que je devrais faire différemment?
Devrais-je passer this en tant que paramètre de ma fonction changeFont ()? Ou devrais-je accéder à this d'une manière différente?
Merci!
Bien qu'Andrew answer soit peut-être la meilleure solution si vous prenez la question à la lettre, je voudrais y ajouter mes deux sous. Votre vrai problème ne semble pas être d'obtenir this
, mais d'avoir plusieurs fois accès à cet élément pour vous appliquer des manipulations. Étant donné que jongler avec this
peut être un problème en JavaScript, il peut être intéressant d’adopter une approche légèrement différente en passant directement la sélection. Cela améliorera également les performances, car il n’est pas nécessaire de sélectionner à nouveau this
encore et encore.
Tout d’abord, modifions légèrement votre fonction changeFont()
pour accepter un objet de sélection.
function changeFont(selection) {
selection
.attr('font-size', '2em');
}
Remarquez comment cela rend la fonction plus généralement applicable car elle ne fait aucune hypothèse sur la sélection transmise. Ce peut être votre d3.select(this)
, une sélection contenant plusieurs éléments ou tout autre objet de sélection D3. En outre, vous n'avez pas besoin de conserver la précédente étendue this
.
Il y a fondamentalement deux manières d'appeler cette fonction.
L'évident passera directement la sélection en tant qu'argument lors de l'appel de la fonction:
const d3This = d3.select(this);
changeFont(d3This);
Heureusement, il existe un moyen plus élégant de le faire en ayant recours à la fonction selection.call()
de D3, qui permet même d’enchaîner les méthodes si vous devez faire plusieurs appels sur la même sélection.
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);
}
Voyons ce que this
est défini pour chacune de vos approches:
// 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
est défini par la manière dont une fonction est appelée. D3 définit commodément this
comme étant l'élément DOM manipulé à l'aide de .apply
sur la fonction passée à selection.attr()
, selection.on()
etc. Toutefois, cela n'est pas le cas pour les fonctions appelées dans la fonction passée à selection.attr()
, selection.on()
, etc.
Nous pouvons voir que this
est bien l'élément DOM si nous enregistrons this
dans la fonction passée à selection.on()
. Si this
n'est pas explicitement défini, ce sera la fenêtre (sauf si vous utilisez le mode strict, alors ce sera indéfini). Nous pouvons voir dans la fonction imbriquée, this
est bien la fenêtre.
answer d'Altocumulus et answer de Gerardo pour éviter le problème this
, vous pouvez également passer this
en tant qu'argument régulier à la fonction (ce modèle est visible dans certains exemples). Mais si vous voulez simplement pouvoir copier et coller le code de la fonction inline vers une fonction définie séparément, vous pouvez utiliser apply, ce qui préservera this
en tant qu'élément en cours de modification:
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>
Si vous deviez transmettre des paramètres à votre fonction avec cela, vous pouvez toujours utiliser 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>
Cependant, cette fonction en ligne appelant d'autres fonctions peut n'être qu'un travail supplémentaire. Si vous appelez une fonction de la fonction inline avec only , il suffit alors de transmettre directement la fonction appelée à selection.on()
, ce qui préserve this
sans aucune étape supplémentaire, car d3 lui applique la valeur attendue (il vous donne également accès à la donnée et à l’index si nécessaire):
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>
Ne placez pas les crochets sur la fonction dans ce cas, nous ne voulons pas renvoyer le résultat de la fonction, nous voulons utiliser la fonction elle-même.
J'ai utilisé apply (Function.prototype.apply()
) dans mes exemples, mais vous pouvez également utiliser call (Function.prototype.call()
), comme Altocumulus note ci-dessous . L'utilisation de call est assez similaire. Si vous ne transmettez aucun paramètre à la fonction et souhaitez uniquement conserver this
, l'utilisation est la même: someFunction.apply(this)
/someFunction.call(this)
. Mais, si les paramètres sont passés, call n'utilise pas de tableau pour les paramètres:
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>
Pour compléter, puisque cette question a déjà deux très bonnes réponses: vous pouvez éviter la confusion avec this
si vous utilisez les troisième et deuxième arguments combinés. C'est quelque chose que même les développeurs D3 finissent par oublier.
Dans plusieurs méthodes D3, l'élément DOM actuel n'est que l'index actuel du groupe de nœuds. Donc, dans la fonction anonyme ...
.on("mouseover", function(_, i, n) {
... this
est juste n[i]
, que vous pouvez simplement transmettre aux autres fonctions. Le _
correspond ici au premier argument, la donnée: j'utilise _
juste pour suivre la convention qui montre que cet argument n'est pas utilisé.
La bonne chose à propos de cette approche est que vous pouvez même (pour quelque raison que vous ayez) utiliser les fonctions de flèche:
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>
Bien sûr, vous ne pouvez pas obtenir l’élément DOM en utilisant this
dans la fonction de flèche.