diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 884f4b693..86043baf8 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -250,8 +250,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const _recalcAttribString = (state) => { const lst = []; - for (const a in state.attribs) { - if (state.attribs[a]) { + for (const [a, count] of Object.entries(state.attribs)) { + if (count) { // The following splitting of the attribute name is a workaround // to enable the content collector to store key-value attributes // see https://github.com/ether/etherpad-lite/issues/2567 for more information @@ -496,9 +496,18 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) // lists do not need to have a type, so before we make a wrong guess // check if we find a better hint within the node's children if (!rr && !type) { - for (const i in node.children) { - if (node.children[i] && node.children[i].name === 'ul') { - type = dom.getAttribute(node.children[i], 'class'); + // If `node` is from the DOM (not cheerio) then it implements the ParentNode interface + // and `node.children` is a HTMLCollection. The DOM + Web IDL specs guarantee that + // HTMLCollection implements the iterable protocol, so for..of iteration should always + // work. See: https://stackoverflow.com/a/41759532. Cheerio behaves the same with + // regard to iteration. + // + // TODO: The set of Nodes included in node.children differs between the DOM and + // cheerio 0.22.0: cheerio includes all child Nodes (including non-Element Nodes) + // whereas the DOM only includes Nodes that are Elements. + for (const child of node.children) { + if (child && child.name === 'ul') { + type = dom.getAttribute(child, 'class'); if (type) { break; }