5

Is it possible to find an element that has a data attribute with the same name of an element's id in the DOM? (Is it a bad practice to give a data attribute the same value as an id?)

Example syntax:

HTML:

<li id="tab-1"></li>
<p data-tab="tab-1">Content 1</p>

Would be curious as to how to accomplish this in Vanilla Javascript. Thank you ☺

Edit: I am hoping to make my code so that if I have the ID of the element, I can just look for a data attribute of the same value if I don't have it off the top of my head.

HannahBanana
  • 101
  • 1
  • 2
  • 8
  • `querySelector` – epascarello Apr 11 '17 at 01:39
  • *"is it bad practice"* ... not at all if there is a close relationship between the two. Not entirely clear what you are exactly asking though. What use case are you wanting for *"find same as"*? – charlietfl Apr 11 '17 at 01:44
  • _"Edit: I am hoping to make my code so that if I have the ID of the element, I can just look for a data attribute of the same value if I don't have it off the top of my head."_ You would need the specific `.name` of the `data-*` attribute. `css` selectors do not provide a means to select elements by value of attribute alone. – guest271314 Apr 11 '17 at 01:58
  • _"Is it possible to find an element that has a data attribute with the same name of an element's id"_ The `data-*` attribute does not have `.name` property that is equal to `.id` of element at Question. – guest271314 Apr 11 '17 at 02:16

3 Answers3

11

Yes, you can do that. Here's how you do it:

var usingId = document.querySelector('#tab-1');
var usingDt = document.querySelector('[data-tab="tab-1"]');
console.log(usingId);
console.log(usingDt);
<li id="tab-1">Tab</li>
<p data-tab="tab-1">P Tab</p>

Is it a bad practice?.Nope

Hari Lamichhane
  • 520
  • 5
  • 11
1

If you are trying to select an element having an unknown html5 data-* attribute using the .id string property from a different element, you can query all elements in document, check the .name and .value of each elements' .attributes, if .value of attribute is equal to .id of element which provides .id and .name of the .attribute property is not "id", the element is matched using attribute equals selector at .querySelector()

<li id="tab-1"></li>
<p data-tab="tab-1">Content 1</p>
<script>
let id = document.getElementById("tab-1").id;
for (let el of document.querySelectorAll("*")) {
  for (let {name, value} of el.attributes) {  
  if (value === id && name !== "id") {
     document.querySelector(`[${name}="${id}"]`)
     .style.color = "green";
     break;
  }
  }
}
</script>
guest271314
  • 1
  • 15
  • 104
  • 177
  • `let id = document.getElementById("tab-1").id;` this made my day. Thanks. (you could also do `let id = document.getElementById("tab-1").id.split('').join('').replace(/a/g, 'a');`) – Kaiido Apr 11 '17 at 01:58
  • @Kaiido Or `let {id} = document.getElementById("tab-1")`. Was only trying to answer Question as interpreted. That is, query `document` for element using only `.id` to match value of `data-*` attribute. – guest271314 Apr 11 '17 at 01:59
  • @Kaiido As interpreted the Question, the `.name` of the `data-*` attribute was not provided, onlt the `.value` being same as `.id` of a different element. `css` selectors do not select an element by value of attribute alone. Though it can be done by composing a function to do so. – guest271314 Apr 11 '17 at 02:03
  • Ok, css selectors are not the way then, but still this `let id= ...` is completely useless and funny. And instead of looping like that over all elements of quesrySelctorAll, you should use a DOMTreeWalker, it's way more efficient, and only elements can have attributes. – Kaiido Apr 11 '17 at 02:05
  • @Kaiido Enlivening to be aware approach provided you with humor. Have not tried `DomTreeWalker`; perhaps post an Answer as to your interpreation of Question and decided approach? – guest271314 Apr 11 '17 at 02:07
  • 1
    TreeWalker is not the best either for this use case, but you should definitely give it a try. https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalker – Kaiido Apr 11 '17 at 02:29
0

If the requisite is to not know the name of the attribute which value is the string you are looking for, then you can create an XPath query, which will return you all the elements having an attribute with such value :

//*[@*='value']

unwrapped :

'//' + // from root to anywhere in the tree
   '*' + // any kind of node
     '[@*' + // with any attribute
         ' = "value"' // which value is 'value'
                      ']'

var valueToFind = 'find-me';
var query = document.evaluate(`//*[@*='${valueToFind}']`, document, null, XPathResult.ANY_TYPE, null);
var elements = [], el;
while(el = query.iterateNext()){
  // in your case you will filter it to not be the original node 
  // if(el !== original)
  elements.push(el);
  }
console.log(elements);
<div data-id="find-me"></div>
<div data-id="do-not-find-me"></div>
<div class="find-me"></div>
<div class="do-not-find-me"></div>
<div style="find-me"></div>
<div id="find-me"></div>

And if the goal is to get only the elements with data-* attributes with such value, then the query is a bit more long :

//*[@*[starts-with(name(), 'data-')] = '${valueToFind}']

unwrapped :

 '//' + // from root to anywhere in the tree
  '*' + // any kind of node
   '[@*' + // with any attribute
      '[starts-with(name(), "data-")]' + // which starts with "data-"
         ' = "value"' // and which value is 'value'
           ']'

var valueToFind = 'find-me';
var query = document.evaluate(`//*[@*[starts-with(name(), 'data-')] = '${valueToFind}']`, document, null, XPathResult.ANY_TYPE, null);
var elements = [], el;
  
while(el = query.iterateNext()){
  elements.push(el);
  }
console.log(elements);
<div data-id="find-me"></div>
<div data-foo="find-me"></div>
<div data-id="do-not-find-me"></div>
<div class="find-me"></div>
<div class="do-not-find-me"></div>
<div style="find-me"></div>
<div id="find-me"></div>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • OP is specifically trying to select element having `data-*` attribute having value equal to a different element `.id`. Is `@*` a wildcard? if yes, how to adjust to match only element having `data-*` attribute? – guest271314 Apr 11 '17 at 02:35
  • @guest271314 there is no way I know of to make partially *wildcarded* values, so you'd have to filter it in the while loop : you just have to check for `if([].some.call(el.attributes, a=>{return a.nodeName.indexOf('data-') === 0 && a.nodeValue === "value";}));` You will already have filtered a **huge** part of the document's nodes. – Kaiido Apr 11 '17 at 02:48
  • Why do you not perform the task at Answer? Granted, Question is not entirely clear as to "Is it possible to find an element that has a data attribute with the same `.name` of an element's id" as the `data-*` attribute is `"data-tab"`, not `"data-tab-1"`. Perhaps OP means having the same `data-*` value as a different elements' `.id` value? – guest271314 Apr 11 '17 at 02:57
  • 1
    @guest271314, added a new snippet with the filtering, I have to admit I'm confused too as to what is the real requirement. – Kaiido Apr 11 '17 at 03:01
  • See also http://stackoverflow.com/questions/35806427/js-hasattribute-with-data-attribute-value/, http://stackoverflow.com/questions/40215773/jquery-select-data-attributes-with-common-keyword/ – guest271314 Apr 11 '17 at 03:08
  • 1
    @guest271314 actually there is a starts-with XPath function... I didn't know ;-) Answered the second link too, but the first one is a bit unrelated and the `dataset` method is probably cleaner. – Kaiido Apr 11 '17 at 04:25