如何使用 vanilla JS 以单个值查询具有值数组的数据属性?

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

我有一系列的博客文章,其中有一个标签数组作为 data-tags html 属性,例如 ["foo", "bar", "baz"]。我想查询 data-tags 数组中包含 foo 的 DOM 元素。

示例标记:

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

我知道可以查询数据属性是否存储为奇异值,即:

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

甚至可以 select 数组中包含特定值的元素吗?

(仅限 Vanilla JS,请不要 jQuery)

您可以使用 CSS 获取页面上的任何元素。

获取具有 href 的元素 [href]

获取数据类型为价格的 div 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>

这是针对您的问题的普通 js 解决方案:

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);

一个班轮选择:

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

或拆分:

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

请注意,如评论中所述,您的 html 标记无效。数据属性不应包含复杂的数据结构,如数组或对象。按照建议,考虑对数组数据执行 .join() 并将其作为字符串输出到属性 [data-tags] 中。为了便于阅读,其中每个值都可以用逗号分隔。

使用有效标记,您的解决方案将略有不同:

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);

上面的代码也更快,因为它使用 .indexOf() 来过滤 DOM 节点。 这是可重用函数中的上述代码:

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);

综上所述,您需要的查询是

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

但是您必须确保 html 数组中的每个元素都包含在双引号内。您可以将其更改为单引号,但请务必更新查询。

您可以通过添加更多规则来搜索多个查询

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

下面是一个片段,它根据您可能感兴趣的某些值应用这些查询。我确保使用您在问题中提出的相同 html 结构。

编辑:

我又添加了一个函数 queryAll,它允许进行值查询。因此,您可以搜索必须具有多个值的元素。

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>