如何判断一个元素是否被CSS选择器匹配?
How to determine if an element is matched by CSS selector?
给定一个 Selenium WebDriver 元素实例,我想检查该元素是否与给定的 CSS 选择器匹配。功能类似于 jQuery 的 is()
功能。
我正在使用 .NET
绑定。
示例(假设该方法将被调用 Is
)
var links = _driver.FindElements(By.CssSelector("a"));
foreach (var link in links)
{
if (link.Is(".myclass[myattr='myvalue']"))
// ... do something
else
// ... do some other thing
}
是否有任何内置的东西可以实现这一点,或者如果没有,有人可以建议一个可能的实现吗?我是 Selenium 的新手,还不知道。
更新
我发现没有内置方法可以执行此操作。我的最终目标是实现 jQuery 的 parents(selector)
和 closest(selector)
等方法,因此对于这种更特殊的情况,我们将不胜感激。
一般来说,为了比较 selenium 中的元素,您可以比较它们的 outerHTML
或 innerHTML
表示。它不是防弹的,但在实践中应该有效:
IWebElement elm = _driver.FindElement(By.CssSelector(".myclass[myattr='myvalue']"));
string linkHtml = link.GetAttribute("outerHTML");
string elmHtml = elm.GetAttribute("outerHTML");
if (linkHtml == elmHtml) {
...
}
请注意,在您的情况下,您似乎可以使用 GetAttribute()
:
检查 class
和 myattr
属性的值
string linkClass = link.GetAttribute("class");
string linkMyAttr = link.GetAttribute("myattr");
if (linkClass.Contains("myclass") && linkMyAttr == "myvalue") {
...
}
没有任何 Selenium 方法可以达到 jQuery 的 $(...).is(...)
的效果。但是,DOM 报价 matches。它不是 $(...).is(...)
的完全替代品,因为它不支持 jQuery 对 CSS 选择器语法的扩展,但是,Selenium 也不支持这些扩展。
您可以通过将要测试的元素传递给 JavaScript 脚本来使用它,然后再传递给 ExecuteScript
。该元素将作为脚本中 arguments
的第一个元素出现。您只需调用 matches
和 return 值。我不使用 C#,但根据文档,我相信它在 C# 中看起来像这样:
bool isIt = (bool)(driver as IJavaScriptExecutor).ExecuteScript(
"return arguments[0].matches(\".myclass[myattr='myvalue']\")", element);
isIt
包含测试结果。 element
是您要测试的元素,driver
是您已经创建的驱动程序。有关兼容性,请参阅 caniuse。在我的应用程序中,我使用 polyfill 在我关心的所有平台上提供 matches
。
话虽这么说我不建议遍历元素并逐个测试它们。问题是每个 ExecuteScript
或 GetAttribute
调用都是脚本和浏览器之间的往返。当您 运行 完整的测试套件时,这会加起来,尤其是当浏览器 运行 在远离您的脚本的服务器场上时。然后它真的 登广告了。我会构建您的测试,以便它查询所有 a
元素的列表和所有 a.myclass[myattr='myvalue']
元素的列表。这归结为两次往返。从这两个列表中,您可以获得进行 if()
测试所需的所有信息。
我最终的解决方案是这三种方法:Parent, Parents(selector), Closest(selector)
。在此之后,可以很容易地实现其他几个类似 jQuery 的辅助方法。希望以后能帮到别人。
public static IWebElement Parent(this IWebElement elem)
{
var script = new[]
{
"return",
" (function(elem) {",
" return elem.parentNode;",
" })(arguments[0]);"
};
var remoteWebElement = elem as RemoteWebElement;
if (remoteWebElement == null)
throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));
var scriptTxt = script.Implode(separator: " ");
var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
if (scriptExecutor == null)
throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));
return scriptExecutor.ExecuteScript(scriptTxt, elem) as IWebElement;
}
public static ReadOnlyCollection<IWebElement> Parents(this IWebElement elem, string selector = null)
{
var script = new[]
{
"return",
" (function(elem) {",
//" console.log(elem);",
" var result = [];",
" var p = elem.parentNode;",
" while (p && p != document) {",
//" console.log(p);",
(string.IsNullOrWhiteSpace(selector) ? null :
" if (p.matches && p.matches('" + selector + "'))"),
" result.push(p);",
" p = p.parentNode;",
" }",
" return result;",
" })(arguments[0]);"
};
var remoteWebElement = elem as RemoteWebElement;
if (remoteWebElement == null)
throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));
var scriptTxt = script.Implode(separator: " ");
var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
if (scriptExecutor == null)
throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));
var resultObj = scriptExecutor.ExecuteScript(scriptTxt, elem) as ReadOnlyCollection<IWebElement>;
if (resultObj == null)
return new ReadOnlyCollection<IWebElement>(new List<IWebElement>());
return resultObj;
}
public static IWebElement Closest(this IWebElement elem, string selector)
{
var script = new[]
{
"return",
" (function(elem) {",
" var p = elem;",
" while (p && p != document) {",
" if (p.matches && p.matches('" + selector + "'))",
" return p;",
" p = p.parentNode;",
" }",
" return null;",
" })(arguments[0]);"
};
var remoteWebElement = elem as RemoteWebElement;
if (remoteWebElement == null)
throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));
var scriptTxt = script.Implode(separator: " ");
var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
if (scriptExecutor == null)
throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));
return scriptExecutor.ExecuteScript(scriptTxt, elem) as IWebElement;
}
给定一个 Selenium WebDriver 元素实例,我想检查该元素是否与给定的 CSS 选择器匹配。功能类似于 jQuery 的 is()
功能。
我正在使用 .NET
绑定。
示例(假设该方法将被调用 Is
)
var links = _driver.FindElements(By.CssSelector("a"));
foreach (var link in links)
{
if (link.Is(".myclass[myattr='myvalue']"))
// ... do something
else
// ... do some other thing
}
是否有任何内置的东西可以实现这一点,或者如果没有,有人可以建议一个可能的实现吗?我是 Selenium 的新手,还不知道。
更新
我发现没有内置方法可以执行此操作。我的最终目标是实现 jQuery 的 parents(selector)
和 closest(selector)
等方法,因此对于这种更特殊的情况,我们将不胜感激。
一般来说,为了比较 selenium 中的元素,您可以比较它们的 outerHTML
或 innerHTML
表示。它不是防弹的,但在实践中应该有效:
IWebElement elm = _driver.FindElement(By.CssSelector(".myclass[myattr='myvalue']"));
string linkHtml = link.GetAttribute("outerHTML");
string elmHtml = elm.GetAttribute("outerHTML");
if (linkHtml == elmHtml) {
...
}
请注意,在您的情况下,您似乎可以使用 GetAttribute()
:
class
和 myattr
属性的值
string linkClass = link.GetAttribute("class");
string linkMyAttr = link.GetAttribute("myattr");
if (linkClass.Contains("myclass") && linkMyAttr == "myvalue") {
...
}
没有任何 Selenium 方法可以达到 jQuery 的 $(...).is(...)
的效果。但是,DOM 报价 matches。它不是 $(...).is(...)
的完全替代品,因为它不支持 jQuery 对 CSS 选择器语法的扩展,但是,Selenium 也不支持这些扩展。
您可以通过将要测试的元素传递给 JavaScript 脚本来使用它,然后再传递给 ExecuteScript
。该元素将作为脚本中 arguments
的第一个元素出现。您只需调用 matches
和 return 值。我不使用 C#,但根据文档,我相信它在 C# 中看起来像这样:
bool isIt = (bool)(driver as IJavaScriptExecutor).ExecuteScript(
"return arguments[0].matches(\".myclass[myattr='myvalue']\")", element);
isIt
包含测试结果。 element
是您要测试的元素,driver
是您已经创建的驱动程序。有关兼容性,请参阅 caniuse。在我的应用程序中,我使用 polyfill 在我关心的所有平台上提供 matches
。
话虽这么说我不建议遍历元素并逐个测试它们。问题是每个 ExecuteScript
或 GetAttribute
调用都是脚本和浏览器之间的往返。当您 运行 完整的测试套件时,这会加起来,尤其是当浏览器 运行 在远离您的脚本的服务器场上时。然后它真的 登广告了。我会构建您的测试,以便它查询所有 a
元素的列表和所有 a.myclass[myattr='myvalue']
元素的列表。这归结为两次往返。从这两个列表中,您可以获得进行 if()
测试所需的所有信息。
我最终的解决方案是这三种方法:Parent, Parents(selector), Closest(selector)
。在此之后,可以很容易地实现其他几个类似 jQuery 的辅助方法。希望以后能帮到别人。
public static IWebElement Parent(this IWebElement elem)
{
var script = new[]
{
"return",
" (function(elem) {",
" return elem.parentNode;",
" })(arguments[0]);"
};
var remoteWebElement = elem as RemoteWebElement;
if (remoteWebElement == null)
throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));
var scriptTxt = script.Implode(separator: " ");
var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
if (scriptExecutor == null)
throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));
return scriptExecutor.ExecuteScript(scriptTxt, elem) as IWebElement;
}
public static ReadOnlyCollection<IWebElement> Parents(this IWebElement elem, string selector = null)
{
var script = new[]
{
"return",
" (function(elem) {",
//" console.log(elem);",
" var result = [];",
" var p = elem.parentNode;",
" while (p && p != document) {",
//" console.log(p);",
(string.IsNullOrWhiteSpace(selector) ? null :
" if (p.matches && p.matches('" + selector + "'))"),
" result.push(p);",
" p = p.parentNode;",
" }",
" return result;",
" })(arguments[0]);"
};
var remoteWebElement = elem as RemoteWebElement;
if (remoteWebElement == null)
throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));
var scriptTxt = script.Implode(separator: " ");
var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
if (scriptExecutor == null)
throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));
var resultObj = scriptExecutor.ExecuteScript(scriptTxt, elem) as ReadOnlyCollection<IWebElement>;
if (resultObj == null)
return new ReadOnlyCollection<IWebElement>(new List<IWebElement>());
return resultObj;
}
public static IWebElement Closest(this IWebElement elem, string selector)
{
var script = new[]
{
"return",
" (function(elem) {",
" var p = elem;",
" while (p && p != document) {",
" if (p.matches && p.matches('" + selector + "'))",
" return p;",
" p = p.parentNode;",
" }",
" return null;",
" })(arguments[0]);"
};
var remoteWebElement = elem as RemoteWebElement;
if (remoteWebElement == null)
throw new NotSupportedException("This method is only supported on RemoteWebElement instances. Got: {0}".FormatWith(elem.GetType().Name));
var scriptTxt = script.Implode(separator: " ");
var scriptExecutor = remoteWebElement.WrappedDriver as IJavaScriptExecutor;
if (scriptExecutor == null)
throw new NotSupportedException("This method is only supported on drivers implementing IJavaScriptExecutor interface. Got: {0}".FormatWith(elem.GetType().Name));
return scriptExecutor.ExecuteScript(scriptTxt, elem) as IWebElement;
}