我正在尝试构建一个标签系统。如果我有以下HTML:
<div class="wizard-tabs">
<ul>
<li class="active"><a href="#" data-formstep="step1">Step 1</a></li>
<li><a href="#" data-formstep="step2">Step 2</a></li>
<li><a href="#" data-formstep="step3">Step 3</a></li>
</ul>
</div>
如何获取具有data-formstep属性等于某个值的锚的列表项?我目前正在使用这个:
document.querySelector('.wizard-tabs > ul > li:has(a[data-formstep="'+newStep+'"])').classList.add("active")
但我收到一个错误说:
Failed to execute 'querySelector' on 'Document': '.wizard-tabs > ul > li:has(a[data-formstep="step1"])' is not a valid selector.
我做错了什么?
答案 0 :(得分:0)
:has
是一项实验性技术,目前在任何浏览器中都不支持。
请参阅 https://developer.mozilla.org/en-US/docs/Web/CSS/:has (感谢@ jaromanda-x)
修复:
正如Jaromanda所说,只需引用<a>
然后获取它.parentElement
。
var li = document.querySelector('.wizard-tabs > ul > li >a[data-formstep="'+newStep+'"]').parentElement;
答案 1 :(得分:0)
:has()
是一个非标准的jQuery选择器仅。它在规范中的状态是未知的,没有实现存在,你应该不假设它将在它准备好之前实现。
当前选择器语法中没有等效于:has()
(这是将其添加到规范中的全部原因);您最好的选择是选择子元素本身并使用.parentElement
来获取其父元素。
答案 2 :(得分:0)
这是has()
选择器的一个非常粗略的polyfill,可能需要更多的测试,但这仍然有用:
(function() {
// Can probably be improved
// Will search for sequence ":has(xxx)" even if xxx contains other sets of func(yyy)
function searchForHas(selector) {
if (!selector) {return null;}
const closed = [], valids = [], open = [], lastFour = ['', '', '', ''];
selector.split('').forEach(char => {
if (char == ')') {
closed.push(open.pop()); // close the last open one
}
open.forEach(o => o.s += char); // add this char to all open ones
if (char == '(') {
open.push({
s: ''
}); // open a new one as an object so we can cross reference
if (lastFour.join('') === ':has') {
valids.push(open[open.length - 1]); // this one is interesting
}
}
// update our ':has' sequence identifier
lastFour.shift();
lastFour.push(char);
});
if (!valids.length) {
return null;
} else {
let str = selector.split(':has(' + valids[0].s + ')');
return [str.shift(), valids[0].s, str.join('')]; // [pre, current_has, post]
}
}
function has(that, sel_parts) {
const matches = [...that.querySelectorAll(sel_parts[0])] // pre selector
.filter(el => el.querySelector(sel_parts[1])); // which has one current_has
return sel_parts[2] ? Array.prototype.concat.apply([], // if there is a post
matches.map(el => [...el.querySelectorAll(':scope' + sel_parts[2])] // flatten all the matches
.filter(el => !!el)
)
) : matches;
}
// Overwrite the protos
const dQS = Document.prototype.querySelector;
Document.prototype.querySelector = function querySelector(selector) {
const has_parts = searchForHas(selector);
if (has_parts) {
return has(this, has_parts)[0] || null;
} else {
return dQS.apply(this, [selector]);
}
};
const dQSA = Document.prototype.querySelectorAll;
Document.prototype.querySelectorAll = function(selector) {
const has_parts = searchForHas(selector);
if (has_parts) {
let arr = has(this, has_parts);
return arr && arr.length ? arr : null;
} else {
return dQSA.apply(this, [selector]);
}
};
const eQS = Element.prototype.querySelector;
Element.prototype.querySelector = function(selector) {
const has_parts = searchForHas(selector);
if (has_parts) {
return has(this, has_parts)[0] || null;
} else {
return eQS.apply(this, [selector]);
}
};
var eQSA = Element.prototype.querySelectorAll;
Element.prototype.querySelectorAll = function(selector) {
const has_parts = searchForHas(selector);
if (has_parts) {
let arr = has(this, has_parts);
return arr && arr.length ? arr : null;
} else {
return eQSA.apply(this, [selector]);
}
};
})();
console.log(document.querySelector('.wizard-tabs > ul > li:has(a[data-formstep="step1"])'));
// and even
console.log(document.querySelector('li:has(a[data-formstep="step2"])>i'));
&#13;
<div class="wizard-tabs">
<ul>
<li class="active"><a href="#" data-formstep="step1">Step 1</a><i data-s="1"></i></li>
<li><a href="#" data-formstep="step2">Step 2</a><i data-s="2"></i></li>
<li><a href="#" data-formstep="step3">Step 3</a><i data-s="3"></i></li>
</ul>
</div>
&#13;