动态分组结构不良的HTML,没有ID?

时间:2012-06-04 21:09:25

标签: javascript google-chrome userscripts

我使用的网站非常旧,数据不会以友好的方式显示。我想写一个用户脚本(javascript / jQuery)来帮助这个网站的可读性。内容看起来像(HTML评论是我自己的,以帮助显示):

<font size="3" face="Courier">
  <br>
  <!-- Begin entry 1 -->
  Name1 (Location1) - Date1:
  <br>
  Text1
  <br>
  Text1 (continued)
  <br>
  Text1 (continued)
  <br>
  <br>
  <!-- Begin entry 2 -->
  Name2 (Location2) - Date2:
  <br>
  Text2
  <br>
  Text2 (continued)
  <br>
  <br>
  Text2 (continued)
  <br>
  Text2 (continued)
  <br>
  <br>
  <!-- Begin entry 3 -->
  Name3 (Location3) - Date3:
  <br>
  Text3
  <br>
  Text3 (continued)
  <br>
  Text3 (continued)
  <br>
  <br>
  <br>
  Text3 (continued)
  <br>
  Text3 (continued)
  <!-- Below is Text3, but a user copied Entry1 here --> 
  Name1 (Location1) - Date1: <!-- text3 -->
  <br> <!-- text3 -->
  Text1 <!-- text3 -->
  <br> <!-- text3 -->
  Text1 (continued) <!-- text3 -->
  <br> <!-- text3 -->
  Text1 (continued) <!-- text3 -->
  <br>
  <br>
  <!-- Begin entry 4 -->
  Name4 ...
  ......
</font>
  • 名称示例:Bob DoleSMITH,JOHN
  • 地点示例:via WebINTERNAL
  • 日期示例:Jul 25, 2011 - 1317 EDTDec 30, 2011 - 1411 EST
  • Text1 / Text2 / etc的示例:Blah blah * (test) text goes here -Thanks Here: there

如您所见,两个<br>总是出现在下一个“条目”(名称,位置,日期)之前,但由于文本是自由文本,因此它还可以包含各种<br>,包括2个或更多。另一个问题是,如果文本还包含Name (Location) - Date粘贴在别处的其他条目。

因此,如果我想编写一个可以添加到谷歌浏览器的脚本,它说添加了一个可以折叠的按钮(或者如果已经折叠,则会折叠)每个条目,这可能吗?我遇到的问题是,由于没有唯一的元素开始或结束条目,我不知道如何开始这个。

一般的概念是遍历每个“条目”(标题是名称/位置/日期)和后面的文本直到下一个标题)并允许每个“条目”可折叠(例如Reddit注释是可折叠的)。

或者对于一个更简单的概念,如果我想用红色字体标记每个其他条目怎么办?那么所有的entry1都是黑色字体,entry2是红色字体,entry3是黑色字体,entry4是红色字体,依此类推。

4 个答案:

答案 0 :(得分:2)

您必须弄清楚如何搜索DOM以查找所需的元素。例如,您可以按标记名称查找内容,然后检查给定标记周围的上下文,看看它是否是您要查找的内容。

如果您提供有关您要查找的内容的更多信息,我们可能会帮助您提供更具体的代码。

例如,document.getElementsByTagName("br")会在文档中找到所有<br>个标签。您可以检查每个标签以找到双<br>标签,如果这是您要查找的内容,或者如果您在双<br>标签之前或之后查找某些特定文字,您也可以查找。正如我在评论中所说,在更具体的代码可以建议之前,您需要更具体地了解您实际寻找的模式。

例如,以下是搜索文档中<br>标记后的特定文本模式的方法:

var items = document.getElementsByTagName("br");
// modify this regex to suit what you're trying to match
var re = /\w+\s\(\w+\)/;
for (var i = 0, len = items.length; i < len; i++) {
    var node = items[i];
    while ((node = node.nextSibling) && node.nodeType == 3) {
        if (re.test(node.nodeValue)) {
            // add a marker test node (just for test purposes)
            var span = document.createElement("span");
            span.className = "marker";
            span.innerHTML = "X";
            node.parentNode.insertBefore(span, node.nextSibling);
        }            
    }        
}​

您可以将正则表达式修改为您希望搜索查找的内容。

您可以在此处查看有效的演示:http://jsfiddle.net/jfriend00/s9VMn/


好的,这是另一个猜测使用正则表达式寻找什么模式的镜头。这会查找两个连续的<br>标记,后跟与模式匹配的文本。然后它将该文本包装在一个跨度中,以便可以根据偶数或奇数进行样式设置。

function getTextAfter(node) {
    // collect text from successive text nodes
    var txt = "";
    while ((node = node.nextSibling) && node.nodeType == 3) {
           txt += node.nodeValue;
    }
    return(txt);    
}

function wrapTextInSpan(preNode, cls) {
    // collect successive text nodes
    // into a span tag
    var node = preNode, item;
    var span = document.createElement("span");
    span.className = cls;
    node = node.nextSibling;
    while (node && node.nodeType == 3) {
        item = node;
        node = node.nextSibling;
        span.appendChild(item);
    }
    preNode.parentNode.insertBefore(span, preNode.nextSibling);
    return(span);
}

// find double br tags
var items = document.getElementsByTagName("br");
var cnt = 1;
var re = /\w+\s+\([^)]+\)\s+-\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d+,\s+\d+\d+/i;
for (var i = 0, len = items.length; i < len; i++) {
    var node = items[i];
    // collect text from successive text nodes
    var txt = "";
    while ((node = node.nextSibling) && node.nodeType == 3) {
           txt += node.nodeValue;
    }
    // if no text, check for successive BR tags
    if (txt.replace(/\n|\s/g, "") == "") {
        if (i + 1 < len && node === items[i + 1]) {
            // found a double BR tag
            // get the text after it
            txt = getTextAfter(node);
            if (re.test(txt)) {
                wrapTextInSpan(node, "marker" + (cnt % 2 ? "Odd" : "Even"));
                ++cnt;
            }
            ++i;
        }
    }
}
​

此处的演示演示:http://jsfiddle.net/jfriend00/ewApy/


这是另一个实际插入展开/折叠目标并执行部分展开/折叠的版本。使用正确的HTML并使用像jQuery这样的漂亮库可以很容易,但是没有更多的代码:

function getTextAfter(node) {
    // collect text from successive text nodes
    var txt = "";
    while ((node = node.nextSibling) && node.nodeType == 3) {
           txt += node.nodeValue;
    }
    return(txt);    
}

function wrapTextInSpan(preNode, cls) {
    // collect successive text nodes
    // into a span tag
    var node = preNode, item;
    var span = document.createElement("span");
    span.className = cls;
    node = node.nextSibling;
    while (node && node.nodeType == 3) {
        item = node;
        node = node.nextSibling;
        span.appendChild(item);
    }
    preNode.parentNode.insertBefore(span, preNode.nextSibling);
    return(span);
}

function wrapBetweenInSpan(preNode, postNode, cls) {
    var node = preNode, item;
    var span = document.createElement("span");
    span.className = cls;
    node = node.nextSibling;
    if (node && node.nodeType == 1 && node.tagName == "BR") {
        preNode = node;
        node = node.nextSibling;
    }
    while (node && node != postNode) {
        item = node;
        node = node.nextSibling;
        span.appendChild(item);
    }
    preNode.parentNode.insertBefore(span, preNode.nextSibling);
    return(span);
}

function toggleClass(el, cls) {
    var str = " " + el.className + " ";
    if (str.indexOf(" " + cls + " ") >= 0) {
        str = str.replace(cls, "").replace(/\s+/, " ").replace(/^\s+|\s+%/, "");
        el.className = str;
    } else {
        el.className = el.className + " " + cls;
    }
}

function hasClass(el, cls) {
    var str = " " + el.className + " ";
    return(str.indexOf(" " + cls + " ") >= 0);    
}

function addButton(target) {
    var span = document.createElement("span");
    span.className = "expandoButton";
    span.innerHTML = "+++";
    span.onclick = function(e) {
        var expando = this;
        do {
            expando = expando.nextSibling;
        } while (expando && !hasClass(expando, "markerContents"));
        toggleClass(expando, "notshown");
    };
    target.parentNode.insertBefore(span, target.nextSibling);
}

// find double br tags
var items = document.getElementsByTagName("br");
var cnt = 1;
var spans = [];
var re = /\w+\s+\([^)]+\)\s+-\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d+,\s+\d+\d+/i;
for (var i = 0, len = items.length; i < len; i++) {
    var node = items[i];
    // collect text from successive text nodes
    var txt = "";
    while ((node = node.nextSibling) && node.nodeType == 3) {
           txt += node.nodeValue;
    }
    // if no text, check for successive BR tags
    if (txt.replace(/\n|\s/g, "") == "") {
        if (i + 1 < len && node === items[i + 1]) {
            // found a double BR tag
            // get the text after it
            txt = getTextAfter(node);
            if (re.test(txt)) {
                var span = wrapTextInSpan(node, "marker marker" + (cnt % 2 ? "Odd" : "Even"));
                spans.push(span);
                ++cnt;
            }
            ++i;
        }
    }
}

// now wrap the contents of each marker
for (i = 0, len = spans.length; i < len; i++) {
    wrapBetweenInSpan(spans[i], spans[i+1], "markerContents shown");
    addButton(spans[i]);
}
​

此版本的工作演示:http://jsfiddle.net/jfriend00/cPbqC/

答案 1 :(得分:1)

对于这种事情,在状态机循环中解析条目。

以下代码始终是第一个答案:

  1. 按问题中指定的方式对HTML进行分组。
  2. 提供点击控制以扩展/收缩分组。
  3. 折叠条目以便开始 - 以获得更好的初步概述。
  4. See a demo of it at jsFiddle.

    UPDATE:

    问题的HTML与the actual page structure不匹配。更新了下面的脚本以解释该问题,并将CSS添加到脚本代码中:

    var containerNode       = document.querySelector ("p font xpre");
    var contentNodes        = containerNode.childNodes;
    var tempContainer       = document.createElement ("div");
    var groupingContainer   = null;
    var hidableDiv          = null;
    var bInEntry            = false;
    var bPrevNodeWasBr      = false;
    
    for (var J = 0, numKids = contentNodes.length;  J < numKids;  ++J) {
        var node            = contentNodes[J];
    
        //--- Is the node an entry start?
        if (    node.nodeType === Node.TEXT_NODE
            &&  bPrevNodeWasBr
            &&  /^\s*\w.*\s\(.+?\)\s+-\s+\w.+?:\s*$/.test (node.textContent)
        ) {
            //--- End the previous grouping, if any and start a new one.
            if (bInEntry) {
                groupingContainer.appendChild (hidableDiv);
                tempContainer.appendChild (groupingContainer);
            }
            else
                bInEntry        = true;
    
            groupingContainer   = document.createElement ("div");
            groupingContainer.className = "groupingDiv";
    
            /*--- Put the entry header in a special <span> to allow for
                expand/contract functionality.
            */
            var controlSpan         = document.createElement ("span");
            controlSpan.className   = "expandCollapse";
            controlSpan.textContent = node.textContent;
            groupingContainer.appendChild (controlSpan);
    
            //--- Since we can't style text nodes, put everythin in this sub-wrapper.
            hidableDiv          = document.createElement ("div");
        }
        else if (bInEntry) {
            //--- Put a copy of the current node to the latest grouping container.
            hidableDiv.appendChild (node.cloneNode(false) );
        }
    
        if (    node.nodeType === Node.ELEMENT_NODE
            &&  node.nodeName === "BR"
        ) {
            bPrevNodeWasBr  = true;
        }
        else
            bPrevNodeWasBr  = false;
    }
    
    //--- Finish up the last entry, if any.
    if (bInEntry) {
        groupingContainer.appendChild (hidableDiv);
        tempContainer.appendChild (groupingContainer);
    }
    
    /*--- If we have done any grouping, replace the original container contents
        with our collection of grouped nodes.
    */
    if (numKids) {
        while (containerNode.hasChildNodes() ) {
            containerNode.removeChild (containerNode.firstChild);
        }
    
        while (tempContainer.hasChildNodes() ) {
            containerNode.appendChild (tempContainer.firstChild);
        }
    }
    
    //--- Initially collapse all sections and make the control spans clickable.
    var entryGroups         = document.querySelectorAll ("div.groupingDiv span.expandCollapse");
    for (var J = entryGroups.length - 1;  J >= 0;  --J) {
        ExpandCollapse (entryGroups[J]);
    
        entryGroups[J].addEventListener ("click", ExpandCollapse, false);
    }
    
    
    //--- Add the CSS styles that make this work well...
    addStyleSheet ( "                                                   \
        div.groupingDiv {                                               \
            border:         1px solid blue;                             \
            margin:         1ex;                                        \
            padding:        1ex;                                        \
        }                                                               \
        span.expandCollapse {                                           \
            background:     lime;                                       \
            cursor:         pointer;                                    \
        }                                                               \
        div.groupingDiv     span.expandCollapse:before {                \
            content:        '-';                                        \
            background:     white;                                      \
            font-weight:    bolder;                                     \
            font-size:      150%;                                       \
            padding:        0 1ex 0 0;                                  \
        }                                                               \
        div.groupingDiv     span.expandCollapse.collapsed:before {      \
            content:        '+';                                        \
        }                                                               \
    " );
    
    
    //--- Functions used...
    function ExpandCollapse (eventOrNode) {
        var controlSpan;
        if (typeof eventOrNode.target == 'undefined')
            controlSpan     = eventOrNode;
        else
            controlSpan     = eventOrNode.target;
    
        //--- Is it currently expanded or contracted?
        var bHidden;
        if (/\bcollapsed\b/.test (controlSpan.className) ) {
            bHidden         = true;
            controlSpan.className = controlSpan.className.replace (/\s*collapsed\s*/, "");
        }
        else {
            bHidden         = false;
            controlSpan.className += " collapsed";
        }
    
        //--- Now expand or collapse the matching group.
        var hidableDiv      = controlSpan.parentNode.children[1];
        hidableDiv.style.display    = bHidden ? "" : "none";
    }
    
    
    function addStyleSheet (text) {
        var D                   = document;
        var styleNode           = D.createElement ('style');
        styleNode.type          = "text/css";
        styleNode.textContent   = text;
    
        var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
        //--- Don't error check here. if DOM not available, should throw error.
        targ.appendChild (styleNode);
    }
    

    如果要单独包装嵌套/引用的条目,您还需要递归。对于嵌套/引用的条目,在应答此问题后打开一个新问题。

    注意:新示例HTML包含多对<html>标记和2组条目!这可能是剪切和粘贴错误,但如果不是,请打开一个新问题,如果easy mod需要帮助来处理多个集合。

答案 2 :(得分:0)

有许多方法可让您在不知道ID的情况下选择元素,例如:

更新:我没有看到任何方法来区分行中的两个<br>元素作为入口结束标记和行中的两个<br>元素只是其中的一部分一个特定的条目。在您的示例中,“text”条目可以包含可能位于名称/位置/日期行中的任何内容。因此,稍微简化它并将每个 double-br作为条目的结尾,您可以这样做:

window.onload = function() {
    var fontTags = document.getElementsByTagName("font"),
        i, j = 0;

    for (i = 0; i < fontTags.length; i++)
        fontTags[i].innerHTML = '<div class="entry odd">' +
            fontTags[i].innerHTML.replace(/<br>\s*?<br>/g, function() {
            return '</div><div class="entry ' + (j++ %2===0?'even':'odd') + '">';
        }) + '</div>';
};

这假设所有字体元素都包含要处理的数据,并使用.replace()来查找双br事件,并在每个条目周围放置包装器div。我已经给每个div一个类“条目”,然后交替使用“偶数”和“奇数”类,以便您可以应用这样的样式:

div.odd { color : red; }

如本演示所示:http://jsfiddle.net/C4h7s/

如果无法在样式表中添加类,显然可以使用内联样式设置颜色。

这是我最接近你的每一个其他入门红色要求。我实际上并没有在该示例中使用“入口”类,但当时它看起来似乎有用,例如,在点击切换想法的这个非常笨重的实现中:http://jsfiddle.net/C4h7s/1/ < / p>

(我没有时间或动力来整理这些演示,但至少他们应该给你一些关于一种方法的想法。或者单向继续进行,取决于你认为我的代码是多么愚蠢。)

答案 3 :(得分:0)

如果您需要在<br /> s:

之间获取文字内容
  1. 选择<font>元素,例如.getElementsByTagName()
  2. 获取其childNodes并循环遍历它们:
    • 如果node type1,那么它将是您的<br />元素之一 - 请与.nodeName核对(否则您需要扩展元素的循环)儿童)
    • 如果其节点类型为3,则为text node。获取文本值并将其与您的内容方案相匹配
  3. 然后你应该能够从中构建一个更合适的DOM。您甚至可以重用文本节点,只需将它们包装在适当的标签中即可。