如何让 JavaScript 忽略已知的缺失元素
我正在编写一个脚本,我想知道如何优雅地告诉 JS 不要在意某个元素是否缺失,而且肯定不会出错,特别是当它们是已知未知的时候。
前提: 创建一个自动通知脚本,如果州立酒类商店有我指定的酒类库存(buffalo trace 看着你)。官方酒类网站(VABC)没有这个功能。
该网站始终显示您最喜欢的商店(硬编码),但它只会显示接下来的 5 家商店 如果他们有库存 。如果您在您想要的酒类的产品页面上,并且您周围的商店有库存,它们就会显示出来。但如果只有 3 家商店有库存,则只会显示 3 家。如果您周围都没有库存,则不会显示任何库存。问题是我不知道哪些产品(我正在寻找几种)或哪些商店(再次,最喜欢的 +5)有库存。因此每次我都必须声明相同的变量集。
如果说某个产品没有商店 3-5,我希望 Javascript 能够继续前进。现在我收到错误“ 错误:无法在带有选择器“(​​//td[contains(@data-title,'Miles')])[2]”的元素上调用 getText,因为未找到元素” 。脚本期望商店 2(又名“Miles”)存在,但由于没有库存,因此此页面上没有商店。
我尝试将此 Store2Miles.getText() 调用隐藏在 IsExisting = True 门后面,但系统仍然会抛出此错误。结果是脚本/站点只是坐在那里等待超时。由于它在多个页面上执行此操作,这使得脚本运行的时间比应有的时间长得多。就像我说的,我想要一个优雅的解决方案。
代码:
it(Product1, ()=> {
browser.url(Product1_URL)
console.log(Product1)
let MoreStores = $("(//a[contains(@class,'more-stores')])[1]")
MoreStores.waitForClickable(500)
MoreStores.click()
/*
This section contains the amount of product at my favorite store, and the Address, Miles, and
Amount of each of the 5 stores. Since I have no idea how much stock will be of each product, I
have to send this complete set to all product pages.
*/
let MyStoreAmount = $("(//td[contains(@data-title,'Inventory')])[1]")
let Store2Address = $("(//td[contains(@data-title,'Store')])[2]")
let Store2Miles = $("(//td[contains(@data-title,'Miles')])[2]")
let Store2Amount = $("(//td[contains(@data-title,'Inventory')])[2]")
let Store3Address = $("(//td[contains(@data-title,'Store')])[3]")
let Store3Miles = $("(//td[contains(@data-title,'Miles')])[3]")
let Store3Amount = $("(//td[contains(@data-title,'Inventory')])[3]")
let Store4Address = $("(//td[contains(@data-title,'Store')])[4]")
let Store4Miles = $("(//td[contains(@data-title,'Miles')])[4]")
let Store4Amount = $("(//td[contains(@data-title,'Inventory')])[4]")
let Store5Address = $("(//td[contains(@data-title,'Store')])[5]")
let Store5Miles = $("(//td[contains(@data-title,'Miles')])[5]")
let Store5Amount = $("(//td[contains(@data-title,'Inventory')])[5]")
let Store6Address = $("(//td[contains(@data-title,'Store')])[6]")
let Store6Miles = $("(//td[contains(@data-title,'Miles')])[6]")
let Store6Amount = $("(//td[contains(@data-title,'Inventory')])[6]")
/*
This section is me creating boolean variables of if Stores are Existing (meaning they have
stock) and if they are Nearby. I only care about stores that are stocked and close to me. A
stocked store 100 miles away is useless, as is a local store that is empty. I am trying to do
IsExisting flags first so that the getText() code wouldn't be read (and thus error) if the store
didn't exist first. */
let MyStoreStocked = false
let Store2Existing = false
let Store3Existing = false
let Store4Existing = false
let Store5Existing = false
let Store6Existing = false
let Store2Nearby = false
let Store3Nearby = false
let Store4Nearby = false
let Store5Nearby = false
let Store6Nearby = false
/*
This creates a T/F flag of if your store is stocked with the product you specified
*/
if (MyStoreAmount.getText() > 0){MyStoreStocked = true} else {MyStoreStocked = false}
/*
This creates T/F flag of if your store exists on the product page. If it doesn't exist, then it
has 0 stock. However just because it exists doesn't mean it's close by, it could be 300 miles
away.*/
if (Store2Address.isExisting()){Store2Existing = true} else{Store2Existing = false}
if (Store3Address.isExisting()){Store3Existing = true} else{Store3Existing = false}
if (Store4Address.isExisting()){Store4Existing = true} else{Store4Existing = false}
if (Store5Address.isExisting()){Store5Existing = true} else{Store5Existing = false}
if (Store6Address.isExisting()){Store6Existing = true} else{Store6Existing = false}
/*
I know due to Debugging that this is the exact spot that errors occur.** The first line, the
Store2Miles.getText(), even though I tried hiding it behind an If isExisting=True, the system
always throws an error on it. It is saying that this element wasn't found. I KNOW some of these
won't be found, that's why I put the isExisting gate there. But the system still reads it
anyways, and timesout. These timeouts make the whole script run longer.
]*/
if (Store2Existing = true){if (Store2Miles.getText() <= Product1_Distance) {Store2Nearby = true} else {Store2Nearby = false}} else {}
if (Store3Existing = true){if (Store3Miles.getText() <= Product1_Distance) {Store3Nearby = true} else {Store3Nearby = false}} else {}
if (Store4Existing = true){if (Store4Miles.getText() <= Product1_Distance) {Store4Nearby = true} else {Store4Nearby = false}} else {}
if (Store5Existing = true){if (Store5Miles.getText() <= Product1_Distance) {Store5Nearby = true} else {Store5Nearby = false}} else {}
if (Store6Existing = true){if (Store6Miles.getText() <= Product1_Distance) {Store6Nearby = true} else {Store6Nearby = false}} else {}
考虑包含名称、距离和数量的对象。收集所有数据可以更轻松地进行条件测试。
在这里,每个商店都被收集到一组对象中,但只有当商店存在于 DOM 中时才会包括在内。
isNearby
属性是在创建时计算的。
在 HTML 中,商店 2 和 3 缺少内容,只有商店 1 被视为附近。
注意:您必须将 XPATH 放回此代码中。(我太懒了,不想启动项目)
const Product1_Distance = 25;
const storeCount = 4;
const stores = $("td[data-title^=Store]");
const miles = $("td[data-title^=Miles]");
const amounts = $("td[data-title^=Inventory]");
const collection = [];
for (let i = 0; i < storeCount; i++) {
const store = stores[i].textContent;
const dist = miles[i].textContent;
const qty = amounts[i].textContent;
if (store && dist && qty) {
const isNearby = dist <= Product1_Distance;
collection.push({ store, dist, qty, isNearby });
}
}
for (let store of collection) {
console.log(store);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="cache-control" content="no-cache">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script defer src="index.js"></script>
</head>
<body>
<table>
<tr>
<td data-title="Store">Store1</td>
<td data-title="Inventory">26</td>
<td data-title="Miles">12</td>
<td data-title="Store">Store2</td>
<td data-title="Inventory">2</td>
<td data-title="Miles"></td>
<td data-title="Store"></td>
<td data-title="Inventory">260</td>
<td data-title="Miles">18</td>
<td data-title="Store">Store4</td>
<td data-title="Inventory">2</td>
<td data-title="Miles">90</td>
</tr>
</table>
</body>
</html>
虽然不打算否定@Randy Casburn 的说法,但我最终还是将我知道可能失败的每个代码段放入 Try/Catch 中。然后将其插入到列表中,以 Break List 作为 Catch 条件。因此,当最终找不到 StoreX 时,此错误将由 Catch 处理,并且它还会 Break 列表。这意味着所有处理都将停止,代码将移出列表。
list: {
try{
if(Store2Miles.getText() < c)
{console.log('Store2 exists and is near'),
Store2Nearby = true}
else {console.log('Store2 exists and is NOT near'),
Store2Nearby = false}
}
catch (err) {
console.log('Store2 does not exist')
Store2Nearby = false
Store3Nearby = false
Store4Nearby = false
Store5Nearby = false
Store6Nearby = false
break list
}
```