Динамически группировать плохо структурированный HTML, у которого нет идентификаторов?

Я использую очень старый веб-сайт, и данные не отображаются в удобной форме. Я хотел бы написать пользовательский скрипт (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 Dole, SMITH,JOHN
  • Пример местоположений: via Web, INTERNAL
  • Пример дат: Jul 25, 2011 - 1317 EDT, Dec 30, 2011 - 1411 EST
  • Пример Text1/Text2/и т. д.: Blah blah * (test) text goes here -Thanks Here: there

Как видите, две <br> всегда стоят перед следующей «записью» (имя, местоположение, дата), но, поскольку текст является свободным текстом, он также может содержать различные <br>, включая 2 или более. Другая проблема заключается в том, что текст также содержит Name (Location) - Date, вставленный, скажем, из другой записи в другом месте.

Итак, если бы я хотел написать скрипт, который можно было бы добавить в Google Chrome, где он добавил бы кнопку, которая сворачивала бы (или разворачивала, если она уже свернута) каждую запись, возможно ли это? Проблема, с которой я сталкиваюсь, заключается в том, что, поскольку нет уникального элемента, начинающего или заканчивающего запись, я не уверен, как это начать.

Общая концепция состоит в том, чтобы перебрать каждую «запись» (заголовок — это имя/местоположение/дата) и текст, следующий за ней до следующего заголовка), и позволить каждой «записи» быть сворачиваемой (например, комментарии Reddit сворачиваемы).

Или для более простой концепции, что, если я хочу пометить каждую вторую запись красным шрифтом? Таким образом, вся запись 1 будет черным шрифтом, запись 2 — красным шрифтом, запись 3 — черным шрифтом, запись 4 — красным шрифтом и так далее.


person JBurace    schedule 04.06.2012    source источник
comment
Просто чтобы уточнить: я думаю, что в вашем вопросе опечатка. Вы должны br прямо перед произнесением текста 2 в строке 19. Там должны быть to br?   -  person Alexander Bird    schedule 05.06.2012
comment
@JBurace: document.getElementById — не единственный способ получить элементы DOM, знаете ли вы это?   -  person Bergi    schedule 05.06.2012
comment
@SethAlexanderBird Да, как поясняется в предложении ниже: Text1, Text2 и т. д. — это свободный текст. Так что, если бы кто-то нажал enter, когда писал это, вышло бы больше <br>   -  person JBurace    schedule 05.06.2012
comment
Вы должны быть более конкретными в своем алгоритме поиска того, что нужно скрыть. Пока не ясно, каков будет алгоритм.   -  person jfriend00    schedule 05.06.2012
comment
@jfriend00 Вот о чем вопрос. Мне трудно что-либо придумать, потому что нет уникальных элементов, определяющих, где начинается и заканчивается запись.   -  person JBurace    schedule 05.06.2012
comment
Если вы не можете концептуально определить, где начинается и заканчивается запись, вы не сможете сделать это программно.   -  person nnnnnn    schedule 05.06.2012
comment
@nnnnnn Но для каждой записи есть шаблон (например, два <br>, за которыми следует это имя/местоположение/дата). Это выглядит возможным, мне просто трудно это спланировать.   -  person JBurace    schedule 05.06.2012
comment
Покажите, как выглядит вложенная или заключенная в кавычки запись.   -  person Brock Adams    schedule 05.06.2012
comment
Чем имя отличается от любых двух слов? Чем местоположение отличается от любого набора слов в скобках?   -  person jfriend00    schedule 05.06.2012
comment
@BrockAdams Только что добавил это с комментариями.   -  person JBurace    schedule 05.06.2012
comment
Примеры вашего имени и местоположения ничем не отличаются от любых других слов. Как сказал @nnnnnn, если вы не можете определить шаблон соответствия, который отличается от обычного текста, то программа также не может этого сделать. Если бы существовал исключительный список возможных местоположений или исключительный список возможных имен, это был бы один угол. В противном случае вы просто ограничены некоторым текстом, за которым следует некоторый текст в скобках, за которым следует что-то похожее на дату. И НИЧЕГО не препятствует тому, чтобы этот формат встречался в тексте, предоставленном пользователем, в некоторых обстоятельствах.   -  person jfriend00    schedule 05.06.2012


Ответы (4)


Для такого рода вещей проанализируйте записи в цикле конечного автомата.

Следующий код всегда был первым ответом на:

  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 набора записей! Это, вероятно, ошибка вырезания и вставки, но если это не так, откройте новый вопрос, если нужна помощь для простого мода для обработки нескольких наборов.

person Brock Adams    schedule 05.06.2012
comment
Хм, когда я пытаюсь запустить это в Chrome, я получаю SyntaxError: Unexpected token ILLEGAL - person JBurace; 05.06.2012
comment
Код отлично работает в Chrome, я проверил его перед публикацией. Пробовали ли вы использовать демонстрационную страницу? Какая версия Хрома? Опубликуйте точный сценарий, который выдает ошибку, и ссылку на целевую страницу. - person Brock Adams; 05.06.2012
comment
Я имел в виду, когда я запускаю его в консоли Chrome на реальной странице, на которой мне нужно его запустить. Он отлично работает в jsfiddle. Но если я скопирую этот код в консоль Chrome и запущу его на странице, я получу ошибку ILLEGAL токена. - person JBurace; 05.06.2012
comment
Хорошо, я понял. Я копировал его из jsfiddle, что, по-видимому, было плохой идеей. Он вставил кучу лишних пробелов, и это вызвало это. Копирование вашего кода отсюда сработало... ну, на этот раз это дало действительную ошибку, я имею в виду: TypeError: Cannot read property 'childNodes' of null - person JBurace; 05.06.2012
comment
Эта ошибка означает, что структура HTML, указанная в вопросе, не соответствует странице или страница добавляет ее через AJAX. Вы можете изменить document.querySelector ("body > font"); на document.querySelector ("body font");, но если на странице более одного тега шрифта (что вполне вероятно), вы получите неожиданные результаты. Ссылка на целевую страницу. Предоставленной на данный момент информации недостаточно, чтобы выбрать хороший селектор для целевого контента. - person Brock Adams; 05.06.2012
comment
Я хотел бы дать ссылку на него или вставить источник, но он вращается вокруг PHI, а я не могу. :/ - person JBurace; 05.06.2012
comment
Вы можете сохранить исходный текст, отредактировать конфиденциальный текст без изменения структуры и опубликовать его в pastebin. - person Brock Adams; 06.06.2012
comment
Спасибо за это, смотрите обновленный ответ. Обратите внимание, что фактическая структура имеет критическое отличие от вопроса! (тег xpre). Отображение точной содержащей структуры также было жизненно важно для разработки селектора для идентификации содержимого полезной нагрузки. ... ... Наконец, источник, на который вы ссылаетесь, имеет несколько пар тегов <html> (недопустимая структура) и два раздела полезной нагрузки. Была ли это ошибка копирования? Смотрите обновленный ответ. - person Brock Adams; 07.06.2012
comment
К сожалению, исходная страница имеет несколько <html>. - person JBurace; 07.06.2012
comment
Откройте для этого новый вопрос, он выходит за рамки того, как вы задали этот вопрос (на который по праву был дан ответ несколько дней назад). Но необходимое изменение довольно легко. - person Brock Adams; 08.06.2012

Вам нужно будет выяснить, как выполнять поиск в 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/

person jfriend00    schedule 04.06.2012
comment
Да, но данные представляют собой свободный текст, поэтому текст1/текст2 и т. д. также может содержать <br> (текст2 содержит еще один <br>) - person JBurace; 05.06.2012
comment
@JBurace - Если вы не можете определить искомый шаблон, который определяет то, что вы хотите найти, независимо от того, какой пользовательский контент находится на месте, то это невозможно сделать. ВЫ должны создать шаблон, который вы ищете, и рассказать нам, что это такое. ИЛИ покажите нам гораздо больше примеров реального контента, чтобы мы могли попытаться выяснить, какой будет разумный шаблон поиска. - person jfriend00; 05.06.2012
comment
Есть шаблон (каждая запись начинается с этого имени/местоположения/даты). Я обновил пост с большим количеством. - person JBurace; 05.06.2012
comment
Я запустил этот jsfiddle в моем последнем примере, и он поместил эти X после Text1 (continued) и т. д. Сейчас пытаюсь его настроить. РЕДАКТИРОВАТЬ: Ах, это скобки; даже свободный текст (Text1 и т. д.) может содержать что угодно, включая круглые скобки. - person JBurace; 05.06.2012
comment
@JBurace - ВАМ нужно настроить регулярное выражение, чтобы изолировать только текст, который вы хотите, чтобы он соответствовал, от текста, который вам не нужен. Вы можете добавить к нему части даты, если хотите, или только определенные фразы для местоположения. Вы предоставили нам НЕПОЛНУЮ информацию о допустимых совпадениях и о том, как отличить хорошее совпадение от введенного пользователем текста, поэтому ВЫ должны выяснить это, изменив регулярное выражение. Например, этот jsFiddle добавляет тире в конце регулярного выражения. - person jfriend00; 05.06.2012
comment
@JBurace - я добавил более подробный фрагмент кода, который ищет два последовательных тега <br>, за которыми следует текст, соответствующий определенному шаблону регулярного выражения, а затем заключает этот текст в диапазон с именем класса на нем. - person jfriend00; 05.06.2012
comment
Ах, чистая и опрятная версия моего четырехстрочного хака, больше похожего на то, что я написал бы, если бы меня это беспокоило. +1 вам. (Отсюда осталось всего несколько строк, чтобы добавить переключение по щелчку, как в этой демонстрации, где я взял свой первоначальный хак и ухудшил его: jsfiddle.net/C4h7s/1/.) - person nnnnnn; 05.06.2012
comment
Я добавил еще одну версию, которая действительно разворачивается/сворачивается. Это пример того, как отсутствие соответствующего HTML приводит к гораздо большему объему кода. - person jfriend00; 05.06.2012

Если вам нужно получить текстовое содержимое между <br />s:

  1. выберите элемент <font>, например. с .getElementsByTagName()
  2. get its childNodes and loop over them:
    • If its node type is 1, it would be one of your <br /> elements - check with .nodeName (else you'd need to expand your loop over the elements children)
    • Если его тип узла 3, это текстовый узел. Получите текстовое значение и сопоставьте его со своей схемой содержимого

Затем вы сможете построить из этого более подходящий DOM. Вы даже можете повторно использовать текстовые узлы и просто обернуть их в соответствующие теги.

person Bergi    schedule 04.06.2012

Существует ряд методов, позволяющих выбирать элементы, не зная идентификатора, например:

ОБНОВЛЕНИЕ: я не вижу способа различить два элемента <br> в строке, которые являются маркером конца записи, и два элемента <br> в строке, которые просто являются частью конкретной записи. Из ваших примеров «текстовые» записи могут содержать все, что могло быть в строке имени/местоположения/даты. Поэтому, немного упростив его и взяв каждый двойной 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/

Очевидно, вы можете использовать встроенные стили для установки цветов, если вы не можете добавить классы в таблицу стилей.

Это самое близкое к вашему требованию "каждый второй вход красный". На самом деле я не использую класс «entry» ни для чего в этом примере, но в то время казалось, что он может быть полезен позже, например, в этой действительно неуклюжей реализации идеи щелчка для переключения: http://jsfiddle.net/C4h7s/1/

(На самом деле у меня нет ни времени, ни мотивации приводить в порядок эти демонстрации, но, по крайней мере, они должны дать вам некоторое представление о том, как действовать дальше. Или как не продолжать, в зависимости от того, насколько вы глупы. думаю, мой код.)

person nnnnnn    schedule 04.06.2012