hasAttribute("Role") 崩溃网页

hasAttribute("Role") crashing web page

我正在创建一个 JavaScript Bookmarklet,它检查每个 div 元素以查看它是否具有角色属性,以及检查“onkeypress”和“onclick”属性。它突出显示 div 没有角色或具有“onclick”且缺少“onkeypress”的。

我在检查角色属性的 if 语句中遇到问题。下面的代码会导致使用它的任何页面崩溃。

var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++){
    if(divs[i].hasAttribute("role")){
        if(divs[i].hasAttribute("onclick" || "onClick")){
            if(!divs[i].hasAttribute("onkeypress")){
                divs[i].classList.add("highlight");
                var divAbove = document.createElement("div");
                divAbove.classList.add("text-box");
                divAbove.innerHTML = "Div";
                divs[i].appendChild(divAbove);
            }
        }
    }else{
        divs[i].classList.add("highlight");
        var divAbove = document.createElement("div");
        divAbove.classList.add("text-box");
        divAbove.innerHTML = "Div: Lacks Role";
        divs[i].appendChild(divAbove);
    }
}

但是每当我在检查“onclick”的 if 语句中添加用于检查角色的 if 语句时,它不再崩溃。但这删除了检查每个 div 角色的功能。

我不确定问题出在哪里。我将不胜感激任何帮助。谢谢。

这里的问题是 getElementsByTagName() returns a HTMLCollection.

An HTMLCollection in the HTML DOM is live; it is automatically updated when the underlying document is changed.

因此,当您创建新的 div 时,集合会发生变化。这会导致无限迭代,因为您也迭代了新的 div

为避免这种情况,您可以使用未直播的 querySelectorAll()

const divs = document.querySelectorAll("div");
for(var i = 0; i < divs.length; i++){
    if(divs[i].hasAttribute("role")){
        if(divs[i].hasAttribute("onclick") || divs[i].hasAttribute("onClick")){
            if(!divs[i].hasAttribute("onkeypress")){
                divs[i].classList.add("highlight");
                var divAbove = document.createElement("div");
                divAbove.classList.add("text-box");
                divAbove.innerHTML = "Div";
                divs[i].appendChild(divAbove);
            }
        }
    }else{
        divs[i].classList.add("highlight");
        var divAbove = document.createElement("div");
        divAbove.classList.add("text-box");
        divAbove.innerHTML = "Div: Lacks Role";
        divs[i].appendChild(divAbove);
    }
}
<div></div>
<div></div>
<div></div>
<div></div>

.forEach() 在这里不是必需的,但可能比 for 循环更简洁。

const divs = document.querySelectorAll("div");
divs.forEach(div => {
  if (div.hasAttribute("role")) {
    if (div.hasAttribute("onclick") || div.hasAttribute("onClick")) {
      if (!div.hasAttribute("onkeypress")) {
        div.classList.add("highlight");
        var divAbove = document.createElement("div");
        divAbove.classList.add("text-box");
        divAbove.innerHTML = "Div";
        div.appendChild(divAbove);
      }
    }
  } else {
    div.classList.add("highlight");
    var divAbove = document.createElement("div");
    divAbove.classList.add("text-box");
    divAbove.innerHTML = "Div: Lacks Role";
    div.appendChild(divAbove);
  }
});
<div></div>
<div></div>
<div></div>
<div></div>

如评论中所述,将 .hasAttribute("onclick" || "onClick") 替换为 divs[i].hasAttribute("onclick") || divs[i].hasAttribute("onClick")

正如@Sven Eberth 指出的那样,您的 div collection 已上线。但是你可以通过复制来冻结它。在您的代码中让我印象深刻的是您的 3 级嵌套非常丑陋(对此感到抱歉)条件语句。请考虑在一些辅助方法上更改此条件。

var divs = Array.from(document.getElementsByTagName("div"));

const elementAttribute = (attributes, element) => {
  return attributes.include.every(attr => element.hasAttribute(attr)) 
  && attributes.exclude.every(attr => !element.hasAttribute(attr))
}

const createElement = ({classList, html}) => {
   const divAbove = document.createElement("div");
   classList.forEach(className => divAbove.classList.add(className));
   divAbove.innerHTML = html;
   return divAbove;
}

const insertElement = ({parent, parentClassNames}, attrs) => {
   parent.classsList.add(parentClassNames);
   element = createElement(attrs);
   parent.appendChild(element);
}

for(var i = 0; i < divs.length; i++){
    if(elementAttribute({
      include: ["role", "onclick"], 
      exclude: ["onkeypress"]
      }, divs[i]){
        insertElement(
          {parent: divs[i], parentClassNames: "highlight"},
          {classNames: "text-box", html: "Div"}
        )
    } else{
        insertElement(
          {parent: divs[i], parentClassNames: "highlight"},
          {classNames: "text-box", html: "Div: Lacks Role"}
        )
    }
}

p.s。 onclick 与 onClick:

Html 元素属性不区分大小写。保持 HTML 标记小写的好习惯。所以你不必检查是否存在“onClick”或“onclick”。更多带有大写字母的 onClick 是反应内部处理程序。它永远不会作为属性添加到 html 元素。

证明: 无论您勾选“onclick”还是“onClick”,都以相同的方式处理。

const divs = Array.from(document.getElementsByTagName("div"))
const divsWith_onClick = divs.filter(el => el.hasAttribute("onClick"));
const divsWith_onclick = divs.filter(el => el.hasAttribute("onclick"));
console.log(divsWith_onClick.length === divsWith_onclick.length)
<div onClick="()=>{}"/>
<div onclick="()=>{}"/>