0

私が使用している非常に古い Web サイトがあり、データがわかりやすい方法で表示されません。このサイトの読みやすさを支援するユーザースクリプト (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>常に次の「エントリ」(名前、場所、日付) の前に 2 つが来ますが、テキストは自由なテキストであるため、<br>2 つ以上を含むさまざまな内容を含めることもできます。もう 1 つの問題は、テキストにName (Location) - Date別のエントリからの貼り付けも含まれている場合です。

それで、Google Chrome に追加できるスクリプトを書きたいとしたら、各エントリを折りたたむ (または既に折りたたまれている場合は折りたたまない) ボタンを追加したとしたら、それは可能でしょうか? 私が抱えている問題は、エントリを開始または終了する一意の要素がないため、これを開始する方法がわからないことです.

一般的な概念は、各「エントリ」(ヘッダーは名前/場所/日付)とそれに続くテキストを次のヘッダーまでループし、各「エントリ」を折りたたみ可能にすることです(Redditコメントなど)。

または、より単純な概念として、他のすべてのエントリを赤いフォントでマークしたい場合はどうすればよいでしょうか? したがって、entry1 はすべて黒のフォント、entry2 は赤のフォント、entry3 は黒のフォント、entry4 は赤のフォントというようになります。

4

4 に答える 4

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/


OK、正規表現を使用して探しているパターンを推測するもう 1 つのショットです。<br>これは、パターンに一致するテキストが後に続く2 つの連続したタグを探します。次に、そのテキストをスパンでラップして、偶数または奇数に従ってスタイルを設定できるようにします。

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/


実際に展開/折りたたみターゲットを挿入し、セクションの展開/折りたたみを行うもう 1 つのバージョンを次に示します。これは、適切な 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/

于 2012-06-04T21:16:56.980 に答える
1

このような場合は、ステート マシン ループでエントリを解析します。

次のコードは、常に最初の回答でした。

  1. 質問で指定されているように HTML をグループ化します。
  2. グループ化を展開/縮小するためのクリック コントロールを提供します。
  3. エントリを折りたたんで開始します -- 最初の概要をわかりやすくします。

jsFiddle でデモを参照してください。

アップデート:

質問の HTML が実際のページ構造と一致しませんでした。それを考慮して以下のスクリプトを更新し、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 つのエントリ セットがあります。これはおそらくカットアンドペーストのエラーですが、そうでない場合は、簡単なモッドが複数のセットを処理するためにヘルプが必要な場合は、新しい質問を開きます.

于 2012-06-05T00:24:09.583 に答える
0

s間のテキスト コンテンツを取得する必要がある場合<br />:

  1. <font>要素を選択します。.getElementsByTagName()
  2. それを取得しchildNodes、それらをループします:
    • そのノードタイプがの場合1、それはあなたの <br />要素の1つになります-チェックしてください.nodeName(そうでなければ、要素の子にループを展開する必要があります)
    • そのノード タイプが の場合3、それはテキスト ノードです。テキスト値を取得し、コンテンツ スキームに一致させます

そこから、より適切な DOM を構築できるはずです。テキスト ノードを再利用して、適切なタグでラップすることもできます。

于 2012-06-04T21:47:21.530 に答える
0

ID を知らなくても要素を選択できるメソッドがいくつかあります。たとえば、次のようになります。

<br>更新:エントリの終わりのマーカー<br>である行の 2 つの要素と、単に特定のエントリの一部である行の 2 つの要素を区別する方法がわかりません。あなたの例から、「テキスト」エントリには、名前/場所/日付の行にあった可能性のあるものを含めることができます。したがって、少し単純化して、すべての 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()して double-br の出現を検出し、代わりに各エントリの周りにラッパー div を配置します。すべての div にクラス "entry" を指定し、クラス "even" と "odd" を交互に配置して、次のようなスタイルを適用できるようにしました。

div.odd { color : red; }

このデモに示すように: http://jsfiddle.net/C4h7s/

スタイルシートにクラスを追加できない場合は、明らかにインライン スタイルを使用して色を設定できます。

これは、他のすべてのエントリは赤であるという要件に最も近いものです。この例では、実際には「エントリ」クラスを使用していませんが、当時は、たとえば、クリックしてトグルするアイデアのこの非常に不格好な実装で、後で役立つ可能性があるように思われました: http://jsfiddle.ネット/C4h7s/1/

(これらのデモを整理する時間も動機もありませんが、少なくとも、先に進むための 1 つの方法についていくつかのアイデアを提供する必要があります。または、私のコードがどれほどばかげていると思うかによって、先に進まない1 つの方法があります。)

于 2012-06-04T21:21:54.553 に答える