J'ai un formulaire, comme suit:

<FORM name="form1">
    <TABLE onkeypress="focusNextFormElement(event)">
        <TR>
            <TD><INPUT name="SomeObscureName"></TD><TD>Some data</TD>
        </TR>
        <TR>
            <TD><INPUT name="ThisName"><span>Here's some obscure text.</span></TD><TD>Some data</TD>
        </TR>
        <TR>
            <TD><INPUT name="someNameIdontKnow"></TD><TD>Some data</TD>
        </TR>
    </TABLE>
</FORM>

Existe-t-il un moyen meilleur et plus efficace d'écrire cette fonction? (Bien sûr, ceci est un exemple simplifié)

function focusNextFormElement(ev){
    var el = ev.target;
    var f = el.form;
    var xx = f.elements.length - 1;
    for(var x = 0; x < xx; x++){
        if(f.elements[x] === el){
            f.elements[x+1].focus();
        }
    }
}

Il peut y avoir des milliers d'éléments sur ce formulaire, et je ne pense pas qu'il soit particulièrement efficace de les parcourir tous à chaque exécution de la fonction.

De plus, je pouvais sélectionner arbitrairement une entrée avec ma souris, donc un tableau de suivi peut ne pas être très utile.

Je n'ai aucun contrôle sur la génération de la page, car c'est un script greasemonkey pour aider à l'automatisation d'une autre page.

MODIFIER Grâce à @Trincot et, d'un point de vue plus large, @Oriol, la réponse est trouvée.

function nextFormSibling(el){
    var elems = el.form.elements;
    var idx = [].indexOf.call(elems, el);
    return elems[idx+1] || el;
}

function previousFormSibling(el){
    var elems = el.form.elements;
    var idx = [].indexOf.call(elems, el);
    return elems[idx-1] || el;
}
1
alfadog67 5 mars 2016 à 21:32

4 réponses

Meilleure réponse

Vous pouvez obtenir l'index de l'élément en appelant indexOf () sur la collection d'éléments du formulaire. Bien que cette collection ne soit pas un tableau JavaScript natif, vous pouvez utiliser indexOf.call comme ceci:

function nextFormElement(el) {
    var f = el.form;
    if (f) return f.elements[1+[].indexOf.call(f.elements, el)];
}

Voici un extrait de démonstration utilisant cette fonction pour déplacer le focus vers l'élément suivant lorsqu'un élément du formulaire est cliqué. C'est évidemment assez inutile, mais cela montre que cela fonctionne:

function nextFormElement(el) {
    var f = el.form;
    if (f) return f.elements[1+[].indexOf.call(f.elements, el)];
}

function focusNextFormElement(ev){
    var el = nextFormElement(ev.target);
    if (el) el.focus();
}

document.querySelector('form').onclick = focusNextFormElement;
<FORM name="form1">
    <TABLE onkeypress="focusNextFormElement(event)">
        <TR>
            <TD><INPUT name="SomeObscureName"></TD><TD>Some data</TD>
        </TR>
        <TR>
            <TD><INPUT name="ThisName"><span>Here's some obscure text.</span></TD><TD>Some data</TD>
        </TR>
        <TR>
            <TD><INPUT name="someNameIdontKnow"></TD><TD>Some data</TD>
        </TR>
    </TABLE>
</FORM>
1
trincot 6 mars 2016 à 00:00

Pourriez-vous préciser ce que vous prévoyez de réaliser? Voulez-vous continuer à déplacer votre focus d'entrée vers les éléments d'entrée? Si c'est le cas, il vaut mieux utiliser l'attribut 'tabindex'.

Rer: http://www.wufoo.com/html5/attributes/27-tabindex .html

Sinon, j'aimerais en savoir plus sur le problème.

Remarque: Votre élément 'onkeypress' sur 'TABLE' a plus de problème car, pour chaque touche enfoncée dans le champ de saisie, il passe immédiatement au champ de saisie suivant.

0
Vinod Tigadi 5 mars 2016 à 20:15

En supposant que le HTML adhère strictement à votre exemple, vous pouvez essayer:

function focusNextFormElement (ev) {
  // First find the tr element of this input...
  var tr = ev.target.parentNode.parentNode; // find the tr node
  // Advance to the next table row ...
  tr = tr.nextElementSibling
  // Then focus the input element in that row...  
  tr.querySelector ('input').focus ();

}

0
HBP 5 mars 2016 à 19:59

Fondamentalement, non, sans connaître la structure du DOM un prieuré, vous avez besoin de l'index de l'élément dans la collection elements pour accéder au suivant. Et l'itérer semble le seul moyen, mais pensez à utiliser indexOf au lieu de le faire manuellement.

Cependant, si vous souhaitez accéder à l'élément suivant plusieurs fois, vous ne pouvez le faire qu'une seule fois et mettre en cache le résultat:

var nextFormElement = (function() {
  var cache = new WeakMap();
  return function(el) {
    if(!cache.has(el)) {
      var els = el.form.elements,
          idx = [].indexOf.call(els, el);
      cache.set(el, els[idx+1] || null);
    }
    return cache.get(el);
  };
})();

Si ES6 n'est pas pris en charge, vous pouvez utiliser un objet simple au lieu d'une carte faible, où les valeurs sont les éléments suivants et les clés sont des identificateurs que vous pouvez stocker dans les éléments en tant qu'attributs data-*.

Ensuite, utilisez-le comme

var next = nextFormElement(event.target);
if(next) next.focus();
var nextFormElement = (function() {
  var cache = new WeakMap();
  return function(el) {
    if(!cache.has(el)) {
      var els = el.form.elements,
          idx = [].indexOf.call(els, el);
      cache.set(el, els[idx+1] || null);
    }
    return cache.get(el);
  };
})();
document.forms.form1.addEventListener('keypress', function(event) {
  var next = nextFormElement(event.target);
  if(next) next.focus();
});
<form name="form1">
  <div>
    <input name="SomeObscureName" />
    <span>Some data</span>
  </div>
  <div>
    <input name="ThisName" />
    <span>Here's some obscure text.</span>
    <span>Some data</span>
  </div>
  <div>
    <input name="someNameIdontKnow" />
    <span>Some data</span>
  </div>
</form>

Cela dit, ce code est complètement envahissant , et je ne le recommande pas. Si l'utilisateur souhaite concentrer le prochain champ pouvant être mis au point sur le texte, il peut utiliser la touche de tabulation. S'il ne veut pas changer d'orientation et que vous le faites pour lui, ce sera tellement ennuyeux.

1
Oriol 5 mars 2016 à 23:27