Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.attr on null selection should not error #315

Closed
isakbm opened this issue Oct 21, 2023 · 3 comments
Closed

.attr on null selection should not error #315

isakbm opened this issue Oct 21, 2023 · 3 comments

Comments

@isakbm
Copy link

isakbm commented Oct 21, 2023

Problem

When a node exists, say it has id="foo" in the DOM, then we can safely do

d3.select("#foo").attr("bar")

however, when the node does NOT exist, then

d3.select("#foo").attr("bar")

will cause the following error

TypeError: Cannot read properties of null (reading 'getAttribute')

The fix to this issue seems relatively straight forward. The culprit is in d3-selection/src/selection/attr.js.

export default function(name, value) {

  var fullname = namespace(name);

  if (arguments.length < 2) {
    var node = this.node();
    return fullname.local
        ? node.getAttributeNS(fullname.space, fullname.local)
        : node.getAttribute(fullname);
  }

  return this.each((value == null
      ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
      ? (fullname.local ? attrFunctionNS : attrFunction)
      : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
}

There is simply a missing null check on the lines trying to call either getAttributeNS or getAttribute on node.

Proposed solution

export default function(name, value) {

  var fullname = namespace(name);

  if (arguments.length < 2) {
    var node = this.node();
    if (node == null) return undefined;
    return fullname.local
        ? node.getAttributeNS(fullname.space, fullname.local)
        : node.getAttribute(fullname);
  }

  return this.each((value == null
      ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
      ? (fullname.local ? attrFunctionNS : attrFunction)
      : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
}

Could also consider just using ?. operator, however this may not be supported by all javascript runtimes?

@mbostock
Copy link
Member

This is intentional. When you’re trying to retrieve the current value of an attribution on the selection, the expectation is that the selection has exactly one element. If you have zero or more than one element selected, the behavior is undefined. If you want to check whether the selection is empty first, you can call selection.empty.

@isakbm
Copy link
Author

isakbm commented Oct 21, 2023

This is intentional. When you’re trying to retrieve the current value of an attribution on the selection, the expectation is that the selection has exactly one element. If you have zero or more than one element selected, the behavior is undefined. If you want to check whether the selection is empty first, you can call selection.empty.

@mbostock

But how is undefined behaviour better than simply returning undefined?

@mbostock
Copy link
Member

In this case, throwing an error helps you understand that you shouldn’t be calling selection.attr when the selection is empty.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants