eslint-plugin-jsx-a11y: label-has-associated-control fails for identical string values of id and htmlFor

The following code fails on jsx-a11y/label-has-associated-control. Changing id and htmlFor to string literals results in a pass.

const DotfilesCheckBox = (props: Props) => {
  const { id, showDotfiles, toggleDotfiles } = props;

  return (
    <div
      className="checkbox"
      style={{
        marginLeft: '1.0em',
      }}
    >
      <input
        checked={showDotfiles}
        id={`DotfilesCheckbox${id}`}
        type="checkbox"
        onChange={toggleDotfiles}
      />

      <label htmlFor={`DotfilesCheckbox${id}`}>Show Dotfiles</label>
    </div>
  );
};

I could see this failing by design for mutable identifiers, but since these are defined as const, it seems safe to conclude that they will evaluate to identical strings if they are in the same block scope. I also realize there are limitations to what can be done statically.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 5
  • Comments: 15 (7 by maintainers)

Most upvoted comments

I have the same issue with the code below:

const Foo = () => {
  const domId = 'foo';

  return (
    <div>
      <label htmlFor={domId}>Foo</label>
      <input type="text" id={domId} />
    </div>
  );
};

Thought that might fix it, but it still fails:

const DotfilesCheckBox = (props: Props) => {
  const { id, showDotfiles, toggleDotfiles } = props;
  const inputId = `DotfilesCheckbox${id}`;

  return (
    <div
      className="checkbox"
      style={{
        marginLeft: '1.0em',
      }}
    >
      <input
        checked={showDotfiles}
        id={inputId}
        type="checkbox"
        onChange={toggleDotfiles}
      />

      <label htmlFor={inputId}>Show Dotfiles</label>
    </div>
  );
};

@nth-chile it’s a requirement if you want to support 100% of assistive devices.

It is an option for this linter rule, but the default is to do both. The second-best option is to nest, since the majority of devices support that.

Having the same issue with the following code. I didn’t think it was an HTML requirement to wrap inputs with labels – more like an option? So maybe it should be an option in this linter rule as well

<label htmlFor="name">Name</label>
<input
  className="form-control"
  id="name"
  type="text"
  onChange={(e) => setName(e.target.value)}
  value={name}
 />

@himynameisdave note i said “every” 😃 a PR that can catch more cases would be very appreciated!

Yep, same issue. Contrary to @ljharb’s suggestion, I actually don’t think it’s that unfeasible for the linter to be able to handle this, since it’s a somewhat common use-case. I will start on a PR.