Home How to use vanilla JS to query with a single value a data attribute with array of values?
Reply: 3

How to use vanilla JS to query with a single value a data attribute with array of values?

binarymason
1#
binarymason Published in 2018-01-06 13:18:38Z

I have a series of blog posts that have an array of tags as a data-tags html attribute such as ["foo", "bar", "baz"]. I'm looking to query the DOM elements that have foo included in the data-tags array.

Example markup:

<li data-tags="['foo', 'bar', 'baz']">...</li>

I know it's possible to query if the data attributes were stored as a singular value, ie:

document.querySelectorAll('[data-specifc-tag="foo"]');

Is it even possible to select elements which arrays include a specific value?

(Vanilla JS only, no jQuery please)

orabis
2#
orabis Reply to 2018-01-06 14:31:28Z

To sum up, the query you need is

document.querySelectorAll('li[data-tags*=\'"foo"\']')

But you have to make sure that each element in your html array is enlosed within double quotes. You may change it to a single quote, but make sure to update the query.

You can search for multiple queries by adding more rules as follows

document.querySelectorAll('li[data-tags*=\'"foo"\']'+'[data-tags*=\'"bar"\']')

Below is a snippet that applies those queries based on some value you may be interested in. I made sure to use the same html structure you put in your question.

Edit:

I added one more function, queryAll, that allows for value queries. So, you can search for elements that must have more than one value.

function entry(search){
  var string = '"' + search + '"';
  return '[data-tags*=\'' + string + '\']';
}

function queryAll() {
  var queries = Array.prototype.slice.call(arguments).map(function(a){
    return '[data-tags*=\'' + a + '\']';
  });
  //example: document.querySelectorAll('li[data-tags*=\'"foo"\']');
  return document.querySelectorAll('li'+ queries.join(''));
}

function query(search) {
  var string = '"' + search + '"';
  //example: document.querySelectorAll('li[data-tags*=\'"foo"\']');
  return document.querySelectorAll('li[data-tags*=\'' + string + '\']');
}
query("foo1"); // One li
query("foo"); //Two li's

queryAll("foo1", "bar1");  //one li
queryAll("foo1", "bar1Not");  //nothing
<ul>
  <li data-tags='["foo", "bar", "baz"]'>...</li>
  <li data-tags='["foo1", "bar1", "baz1"]'>...</li>
  <li data-tags='["foo2", "bar2", "baz2"]'>...</li>
  <li data-tags='["foo", "bar2", "baz2"]'>...</li>
</ul>

kurt
3#
kurt Reply to 2018-01-06 13:41:56Z

You can use CSS to get any element on the page.

I.e.

Get elements with href's [href]

Get divs with data-type of price div[data-type=price]

var attributes = ['data-rel','type'];
attributes.forEach((at)=>{
   document.querySelectorAll('['+at+']').forEach((element)=>{
      element.append('<b>'+at+'</b>');
   });
 });
<p>Hello</p>
<p data-rel="title">World!</p>
<p type="emoji">:-)</p>

neatsu
4#
neatsu Reply to 2018-01-06 14:19:07Z

Here's a vanilla js solution to your problem:

const posts = document.querySelectorAll('[data-tags]');
const fooPosts = [];
posts.forEach(post => {
    if (/foo/.test(post.getAttribute('data-tags'))) {
        fooPosts.push(post);
    }
});

// output the filtered posts
console.log(fooPosts);

One liner alternative:

document.querySelectorAll('[data-tags]').forEach(post => /foo/.test(post.getAttribute('data-tags')) && console.log(post));

Or splitted:

document.querySelectorAll('[data-tags]').forEach(post =>
    /foo/.test(post.getAttribute('data-tags')) && console.log(post));

Please, note that, as mentioned in the comments, your html markup is invalid. Data attributes should not contain complex data structure like arrays or objects. As suggested, consider performing a .join() on your array data and outputting it in the attribute [data-tags] as a string. In it each value can be separated by comma for readability.

Using the valid markup your solution will be slightly different:

const posts = document.querySelectorAll('[data-tags]');
const fooPosts = [];
posts.forEach(post => {
    if (post.getAttribute('data-tags').indexOf('foo') > -1) {
        fooPosts.push(post);
    }
});

console.log(fooPosts);

The above code is also faster as it's using .indexOf() to filter the DOM nodes. Here's the above code in a reusable function:

const filterNodesByAttr = (attr, tag) => {
    let filtered = [];
    const items = document.querySelectorAll(`[${attr}]`);
    items.forEach(item => {
        if (item.getAttribute(attr).indexOf(tag) > -1) {
            filtered.push(item);
        }
    });
    return filtered;
};

const fooPosts = filterNodesByAttr('data-tags', 'foo');
console.log(fooPosts);
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.30811 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO