1

Out of curiosity, would it be possible to refactor this:

document.querySelectorAll('input[id^=' + tagId + '],select[id^=' + tagId + '],textarea[id^=' + tagId + ']');

to something smaller, like this?:

document.querySelectorAll('(input|select|textarea)[id^=' + tagId + ']');

Of course this doesn't work, that's why I'm asking. Is it possible?

ilias
  • 63
  • 1
  • 11
  • Which elements other than `input`, `select`, `textarea` have `id` beginning with `tagId` ? – guest271314 Mar 19 '16 at 08:58
  • Not other that those, I need to get any of these elements (input, select, textarea) that their id begins with tagId. – ilias Mar 19 '16 at 09:08
  • 2
    _"Not other that those"_ If there are no elements to exclude, you should be able to use `document.querySelectorAll("[id^="+tagId+"]")` , which should return `input` , `select`, `textarea` elements having `id` beginning with `tagId`. You should be able to use `js` at post without `:not()` – guest271314 Mar 19 '16 at 09:10
  • But a simple document.querySelectorAll("[id^="+tagId+"]"), would return any element that begins with this id. I want to narrow them down to only: input, select, textarea. – ilias Mar 19 '16 at 09:15
  • That is why asked _"Which elements other than input, select, textarea have id beginning with tagId "_ at http://stackoverflow.com/questions/36099789/is-it-possible-to-combine-elements-for-the-same-attributes-with-queryselectorall/36099835#comment59842888_36099789 ; response appeared to be _"Not other that those"_ http://stackoverflow.com/questions/36099789/is-it-possible-to-combine-elements-for-the-same-attributes-with-queryselectorall/36099835#comment59843036_36099789 ? – guest271314 Mar 19 '16 at 09:17
  • Managing DOM elements via their IDs, as if they were variables in some giant global namespace, is an anti-pattern. –  Mar 19 '16 at 09:41
  • Can you please show some HTML to accompany your JavaScript and clearly show the elements you wish to select by the requested selector syntax, and those elements not to be selected. – David Thomas Mar 19 '16 at 09:42
  • It's just a page with a bunch of form elements, some of which their id begins with a known string. Instead of letting querySelectorAll loop through all the form elements, I already know (from a technical requirement) that this string can only be in text, select and textarea tags. The best solution from my point of view is to put an attribute to those elements (at the back-end) so I can search using just this one. A few people actually proposed this solution here too, so thanks. – ilias Mar 19 '16 at 20:36

4 Answers4

2

Use :any pseudo-class;

document.querySelectorAll('[id^=' + tagId + ']:-webkit-any(input, textarea, select)');

Right now, you will need to prefix this with -webkit- for Chrome and Safari, or -moz for Firefox; no IE support. This is likely to be standardized eventually under the name :matches. See MDN documentation here.

However, managing DOM elements via their IDs, as if they were named variables in some giant global namespace, is an anti-pattern. It is highly likely that there are better ways to find and keep track of elements than to assign a bunch of IDs over here in your code, then over there do getElementById or equivalent every time you turn around in order to find it again, much less doing the equivalent of a wildcard-like search.

  • This solution has similar pattern as described at Question and returns expected results. fwiw, a piece using jQuery that adjusts prefixes of `css` , could probably be composed using pure `javascript` http://stackoverflow.com/questions/22746962/detecting-the-browser-using-jquery – guest271314 Mar 19 '16 at 16:30
  • Could `:any` be used with prefixes at a selector to match both chrome and firefox ? At first glance not sure how it would be possible ? – guest271314 Mar 19 '16 at 16:37
  • No, you'd have to write two selectors, one for firefox and one for chrome. If you tried to combine them, each engine would reject the entire selector due to presence of the other engine's prefixed pseudo-class which it would not recognize. –  Mar 19 '16 at 16:44
  • Could `querySelectorAll` be adjusted to feature test browser and add, or recognize `:any` selector internally without prefix ? Or would it be counter-productive to re-write `querySelectorAll` for the purpose of recognizing two five-character prefixes ? Was able to use different properties without prefix throughout `html` , `css` , `js` after implementing jQuery styled approach at link – guest271314 Mar 19 '16 at 16:48
0

You could use "[id^=" + tagId + "]" with :not(/* element */):not(/* element */)

var tagId = "abc";
var elems = document.querySelectorAll("[id^=" + tagId + "]:not(div):not(p)");
console.log(elems)
<input id="abc-1"> 
<select id="abc-2"></select>
<textarea id="abc-3"></textarea>
<div id="abc-4"></div>
<p id="abc-5"></p>

alternatively, you could add a className or a data-* attribute to input, select, textarea elements; the data-* attribute does not require a name or value; used only for selection filtering ; you could then utilize document.querySelectorAll("[id^=" + tagId + "][data-_]"); , or briefer document.querySelectorAll("[data-_]")

var tagId = "abc";
var elems = document.querySelectorAll("[id^=" + tagId + "][data-_]");
console.log(elems, elems[0].dataset)
<input id="abc-1" data-_> 
<select id="abc-2" data-_></select>
<textarea id="abc-3" data-_></textarea>
<div id="abc-4"></div>
<p id="abc-5"></p>

An approach using Array.prototype.filter(), though not as brief as using unique className or data-* attribute, similar to pattern described at OP

var tagId = "abc";
var elems = [].filter.call(document.querySelectorAll("[id^=" + tagId + "]")
            , function(el) {
                return /input|select|textarea/i.test(el.tagName)
            });
console.log(elems)
<input id="abc-1"> 
<select id="abc-2"></select>
<textarea id="abc-3"></textarea>
<div id="abc-4"></div>
<p id="abc-5"></p>
guest271314
  • 1
  • 15
  • 104
  • 177
  • This(the answer before your recent edit) will not work for sure. https://jsfiddle.net/p7tegwy7/2/ You are confusing jquery :not() selector with css's. Css's :not selector won't work with complex selectors in it. – Rajaprabhu Aravindasamy Mar 19 '16 at 09:05
  • 1
    I don't want to exclude, I want only any of the above elements (input, select, textarea) – ilias Mar 19 '16 at 09:11
  • @ilias So you don't want to exclude the `div` and `p` in this example? – CupawnTae Mar 19 '16 at 09:12
  • @ilias Then that is not possible with javascript's `querySelectorAll` as of now. – Rajaprabhu Aravindasamy Mar 19 '16 at 09:13
  • _"I don't want to exclude, I want only any of the above elements (input, select, textarea)"_ If no other elements have an `id` beginning with `tagId` you should be able to use selector `"[id^="+tagId+"]"` , without `:not()` – guest271314 Mar 19 '16 at 09:14
  • @RajaprabhuAravindasamy _"Then that is not possible with javascript's querySelectorAll as of now"_ ? See http://stackoverflow.com/questions/36099789/is-it-possible-to-combine-elements-for-the-same-attributes-with-queryselectorall/36099835#comment59843036_36099789 ; `input`, `select` , `textarea` elements are only elements having `id` beginning with `tagId`. Why would selection of those elements not be possible using `querySelectorAll` and selector `"[id^="+tagId+"]"` ? – guest271314 Mar 19 '16 at 09:14
  • @guest271314 [OP don't want to exclude anything](http://stackoverflow.com/questions/36099789/is-it-possible-to-combine-elements-for-the-same-attributes-with-queryselectorall/36099835#comment59843076_36099835). And he want the attribute start with selector to be ran over certain elements. – Rajaprabhu Aravindasamy Mar 19 '16 at 09:16
  • @RajaprabhuAravindasamy _"OP don't want to exclude anything."_ though _"And he want the attribute start with selector to be ran over certain elements."_ is an exclusion of elements that are not "certain elements" – guest271314 Mar 19 '16 at 09:20
  • @ilias The simplest solution would be to add same `className` to `input`, `select`, `textarea` elements – guest271314 Mar 19 '16 at 09:21
  • Adding a className would certainly do the trick, however I wanted to know if the syntax of querySelectorAll supports such element/attribute combination. I guess it doesn't, at least for now. – ilias Mar 19 '16 at 09:25
  • @ilias Another option would be to add a `data-*` attribute to `input`, `select`, `textarea` elements, utilize selector `document.querySelectorAll` with selector `"[id^=" + tagId + "][data]"` , or even briefer `document.querySelectorAll("[data]")`; see updated post – guest271314 Mar 19 '16 at 09:28
  • I don't believe `data` by itself is a valid attribute. Can you provide a reference? W3C validation fails with the error "Attribute `data` not allowed on element `textarea` at this point.". –  Mar 19 '16 at 09:54
  • @torazaburo No reference for `data` by itself. The specification does not appear to mandate that a name be included https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-* ? What does validator log for `data=""` ? – guest271314 Mar 19 '16 at 10:06
  • `data` is not a valid attribute, whether with or without a value. –  Mar 19 '16 at 10:07
  • @torazaburo _"data is not a valid attribute, whether with or without a value."_ Is this what validator relays ? – guest271314 Mar 19 '16 at 10:08
  • The spec says "A custom data attribute is an attribute in no namespace whose name starts with the string "data-", has at least one character after the hyphen," –  Mar 19 '16 at 10:09
  • @torazaburo Have used and viewed `data=""` before in the wild; though to be in accord with specification will updated post – guest271314 Mar 19 '16 at 10:11
0

Until the :any selector is standardized, you could use a helper function, similar to using a CSS preprocessor (borrowing @guest271314's example):

function makeSelector(tagId, elements) {
  return elements.map(function(el) {
    return el + '[id^=' + tagId + ']';
  }).join(',');
}

var selector = makeSelector('abc', ['input','select','textarea']);

console.log(document.querySelectorAll(selector));
<input id="abc-1"> 
<select id="abc-2"></select>
<textarea id="abc-3"></textarea>
<div id="abc-4"></div>
<p id="abc-5"></p>

You could then swap the helper implementation at a later date (e.g. for one using :any) without affecting functionality.

Community
  • 1
  • 1
CupawnTae
  • 14,192
  • 3
  • 29
  • 60
0

An approach extending document.querySelcectorAll() to internally add vendor prefix at :any without including vendor prefix in selector string by using feature detection at document.body.style . Could probably be improved in several ways; additional css selectors that currently require vendor prefixes could be added , multiple if statements could be more thorough or shortened; and other improvements

originalQuerySelectorAll = document.querySelectorAll;
console.log(originalQuerySelectorAll);

document.querySelectorAll = function() {
  var selector = arguments[0];
  if (/\:any/.test(selector)) {
      if ("webkitAnimation" in document.body.style) {
        selector = selector.replace(/\:(any)/g, ":-webkit-$1");
      }
      if ("MozAnimation" in document.body.style) {
        selector = selector.replace(/\:(any)/g, ":-moz-$1");
      }
  }
  return originalQuerySelectorAll.call(document, selector)
}

var tagId = "abc";
var elems = document.querySelectorAll("[id^=" + tagId + "]:any(input, select, textarea)");
console.log(elems)
<input id="abc-1"> 
<select id="abc-2"></select>
<textarea id="abc-3"></textarea>
<div id="abc-4"></div>
<p id="abc-5"></p>

jsfiddle https://jsfiddle.net/0xpu1bvw/

guest271314
  • 1
  • 15
  • 104
  • 177