Dinoer

It clicked, but not on the element you meant

Three identical buttons, a list that grew by four rows, a selector that worked yesterday. Every target here is a CSS selector — which means every mistake here is a selector mistake.

The symptom. The click works. Something happens. It is not what you wanted — the wrong domain got cloned, the wrong row got deleted, the wrong dialog opened.

Nothing errors, because nothing went wrong mechanically. Dinoer clicked what you designated. You designated the wrong thing — and since every target here is a CSS selector, with no numbered alternative to fall back on, that is always a selector problem, not a perception problem.

Cause 1 — several elements match your selector

A page with three “Clone” buttons, one per domain. This selector picks the first one in DOM order:

{"type": "cliquer", "selecteur": "button:has-text('Clone')"}

Observed for real: the clone started on a different domain than the one intended, and only the server log said so. A silent inversion.

The fix — designate, do not describe. Qualify the selector with something unique to that row:

{"type": "cliquer", "selecteur": "button[title='Clone example-two']"}

If nothing in the markup is unique, evaluer can find the right element by content and hand back a selector-worthy attribute before you act:

{"type": "evaluer",
 "script": "Array.from(document.querySelectorAll('tr')).findIndex(tr => tr.textContent.includes('example-two'))"}

Cause 2 — a position-based selector, and the position moved

:nth-child() and :nth-of-type() name a row by its place in the list, not by what it is. Add four rows above your target between writing the scenario and running it, and your target shifts by four. A procedure validated on a page with one row breaks on the same page with five — same selector, different result, no error anywhere.

The fix. Never encode position where content will do:

{"type": "cliquer", "selecteur": "tr:nth-child(3) button.delete"}
{"type": "cliquer", "selecteur": "tr:has-text('example-two') button.delete"}

The second version keeps working after the list changes shape, because it names the row by what it contains rather than where it happens to sit.

Identify by content, not by position

“The third row is the most recent one” is true until someone adds a fourth.

When your target carries something identifying — a timestamp, a slug, an identifier — build the selector from it, or read it first with evaluer and decide programmatically:

{"type": "evaluer",
 "script": "Array.from(document.querySelectorAll('input.chk-item')).map(i => i.value)"}

No numbering scheme to trust, no heuristic to break next week — just the content the page already carries, read directly.

In short

  • A selector matching several elements picks the first, silently.
  • :nth-child()/:nth-of-type() break when the list’s shape changes — prefer a content-based selector wherever one is available.
  • evaluer can locate an element by content before you commit to a selector, cheaper than guessing.
  • Identify targets by their content, never by their position in a list.