HervéRenault.fr

Logo non-officiel de JavaScript

Exemple de XPath en JavaScript

J'ai un vieux tableau dans lequel j'aimerais cocher les cases de toutes les lignes qui ont un lien qui commence par 2021 :

Illustration du vieux tableau

Ce n'est pas possible simplement avec document.querySelectorAll. Voici le code JS simplifié qui utilise XPath en partant du principe que rien d'autre ne va modifier la page pendant son exécution :

var iterateur = document.evaluate("//td/a[starts-with(., '2021')]/../preceding-sibling::td/input", document);

var nœud = iterateur.iterateNext();

while (nœud) {
  nœud.checked = true;

  nœud = iterateur.iterateNext();
}

Mais si je veux aussi au passage mettre sur fond vert tous les liens qui commencent par 2021, je modifie le document et ça lance une exception :
DOMException: XPathResult.iterateNext: The document has been mutated since the result was returned

var iterateur = document.evaluate("//td/a[starts-with(., '2021')]", document);

var nœud = iterateur.iterateNext();

while (nœud) {
  nœud.style = 'background: limegreen';

  var ite2 = document.evaluate("../preceding-sibling::td/input", nœud);
  var n2 = ite2.iterateNext();

  n2.checked = true;

  nœud = iterateur.iterateNext();
}
            

Dans ce cas, il faut que je demande un cliché (snapshot) du DOM :

var cliché = document.evaluate("//td/a[starts-with(., '2021')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);

for (var i = 0; i < cliché.snapshotLength; i++) {
  var nœud = cliché.snapshotItem(i);

  nœud.style = 'background: limegreen';

  var itérateur = document.evaluate("../preceding-sibling::td/input", nœud);
  var n2 = itérateur.iterateNext();

  n2.checked = true;
}

Note au passage : si je veux cocher les cases dont les liens se terminent par 2021, je ne peux malheureusement pas utiliser ends-with dans le navigateur parce que c'est une fonction XPath 2.0 et les navigateurs ne gèrent que XPath 1.0 donc il faut ruser avec //td/a[substring(., string-length(.) - string-length('2021') + 1) = '2021']/../preceding-sibling::td/input 😕

cf référence XPath sur MDN