Нужно регулярное выражение, чтобы добавить класс CSS в первый и последний элемент списка - PullRequest
2 голосов
/ 30 ноября 2008

UPDATE: Спасибо всем за ваш вклад. Некоторая дополнительная информация.

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

У меня также есть возможность взломать скрипт (для электронной коммерции), чтобы вставить классы при построении навигации. Я хотел ограничить количество хаков, которые у меня есть, чтобы облегчить себе задачу при обновлении до последней версии программного обеспечения.

С учетом сказанного я довольно хорошо знаю свою ситуацию и различные доступные мне варианты. Первая часть моего регулярного выражения работает, как ожидалось. Я действительно более или менее написал, чтобы увидеть, если кто-то скажет: «Эй, дурак, это легко просто изменить это .....»

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

ОРИГИНАЛ:

Я пытаюсь использовать регулярные выражения для добавления CSS-класса в первый и последний элементы списка в упорядоченном списке. Я перепробовал множество разных способов, но не смог получить желаемых результатов.

У меня есть регулярное выражение для первого элемента списка, но я не могу найти правильное выражение для последнего. Вот с чем я работаю:

    $patterns = array('/<ul+([^<]*)<li/m', '/<([^<]*)(?<=<li)(.*)<\/ul>/s');
    $replace = array('<ul$1<li class="first"','<li class="last"$2$3</ul>');
    $navigation = preg_replace($patterns, $replace, $navigation); 

Любая помощь будет принята с благодарностью.

Ответы [ 6 ]

3 голосов
/ 30 ноября 2008

Джейми Завински хотел бы что-то сказать по этому поводу ...

У вас есть правильный анализатор HTML? Я не знаю, есть ли что-то вроде hpricot для PHP, но это правильный способ справиться с этим. Вы могли бы по крайней мере нанять hpricot, чтобы сделать первую уборку для вас.

Если вы действительно генерируете HTML - делайте это там. Похоже, вы хотите сгенерировать навигацию и иметь на ней что-то вроде .first и .last. Сделай шаг назад и попробуй это.

2 голосов
/ 30 ноября 2008

+ 1 для создания правильного HTML как лучший вариант.

Но совершенно другой подход, который может или не может быть приемлем для вас: вы можете использовать javascript.

Это использует jquery, чтобы упростить ...

$(document).ready(
    function() {
        $('#id-of-ul:firstChild').addClass('first');        
        $('#id-of-ul:lastChild').addClass('last');
    }

);

Как я уже сказал, в этом случае может быть, а может и не быть, но в некоторых случаях я считаю его правильным решением.

PS: Вы говорите упорядоченный список, затем приводите ul в вашем примере. ol = упорядоченный список, ul = неупорядоченный список

1 голос
/ 10 декабря 2008

Я не знаю, беспокоится ли кто-нибудь больше, но у меня есть решение, которое работает в моем простом тестовом примере (и я считаю, что оно должно работать в общем случае).

Во-первых, позвольте мне указать на две вещи: хотя PhiLho прав в том, что s «опасен», поскольку точки могут соответствовать всему, вплоть до финала документа, вполне может быть, что ты хочешь. Это становится проблемой только с плохо сформированными страницами. Будьте осторожны с любым таким регулярным выражением на больших, написанных вручную страницах.

Во-вторых, php имеет особое значение обратной косой черты, даже в одинарных кавычках. Большинство регулярных выражений будут хорошо работать в любом случае, но вы должны всегда избегать их, на всякий случай.

Вот мой код:

<?php
$navigation='<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
<li>Beer</li>
<li>Water</li>
</ul>';

$patterns = array('/<ul.*?>\\s*<li/',
                  '/<li((.(?<!<li))*?<\\/ul>)/s');
$replace = array('$0 class="first"',
                 '<li class="last"$1');
$navigation = preg_replace($patterns, $replace, $navigation);
echo $navigation;
?>

Это выведет

<ul>
<li class="first">Coffee</li>
<li>Tea</li>
<li>Milk</li>
<li>Beer</li>
<li class="last">Water</li>
</ul>

Предполагается, что внутри открывающего тега

  • , повторяемой любое количество раз (*) не жадным образом (символ?).

    Конечно, все это должно быть расширено, если есть вероятность, что у элементов списка уже установлен атрибут class . Также, если есть только один элемент списка, он будет совпадать дважды, давая ему два таких атрибута. По крайней мере, для xhtml это нарушит проверку.

1 голос
/ 30 ноября 2008

Вы писали:

$patterns = array('/<ul+([^<]*)<li/m','/<([^<]*)(?<=<li)(.*)<\/ul>/s');

Первый шаблон:
ul+ => вы ищете что-то вроде Ullll ...
Модификатор m здесь бесполезен, так как вы не используете ^ и $.

Второй шаблон:
Использование. * Вместе с s опасно, потому что вы можете выбрать весь документ до последней / ul страницы ...
И хорошо, я бы просто сбросил модификатор s и использовал бы: (<li\s)(.*?</li>\s*</ul>) с заменой: '$1class="last" $2'

С учетом вышеизложенных замечаний я бы написал первое выражение: <ul.*?>\s*<li

Хотя мне надоело видеть цитату из Jamie Zawinski каждый раз, когда возникает вопрос о регулярном выражении, Дастин прав, указывая вам на парсер HTML (или просто генерируя правильный HTML с самого начала!): Регулярные выражения и HTML этого не делают хорошо сочетаются, потому что синтаксис HTML сложен, и если вы не действуете на хорошо известной машине, сгенерированной с очень предсказуемым результатом, в некоторых случаях вы склонны что-то ломать.

0 голосов
/ 30 августа 2011

В качестве предисловия ... в большинстве случаев это слишком сложная вещь. Пожалуйста, смотрите другие ответы для большего здравомыслия:)

Вот небольшой класс PHP, который я написал для решения аналогичной проблемы. Он добавляет «первый», «последний» и любые другие классы, которые вы хотите. Он будет обрабатывать li без атрибута "class", а также с теми, которые уже имеют некоторые классы.

<?php
/**
 * Modify list items in pre-rendered html.
 *
 * Usage Example:
 *  $replaced_text = ListAlter::addClasses($original_html, array('cool', 'awsome'));
 */
class ListAlter {
  private $classes = array();
  private $classes_found = FALSE;
  private $count = 0;
  private $total = 0;

  // No public instances.
  private function __construct() {}

  /**
   * Adds 'first', 'last', and any extra classes you want.
   */
  static function addClasses($html, $extra_classes = array()) {
    $instance = new self();
    $instance->classes = $extra_classes;
    $total = preg_match_all('~<li([^>]*?)>~', $html, $matches);
    $instance->total = $total ? $total : 0;
    return preg_replace_callback('~<li([^>]*?)>~', array($instance, 'processListItem'), $html);
  }

  private function processListItem($matches) {
    $this->count++;
    $this->classes_found = FALSE;
    $processed = preg_replace_callback('~(\w+)="(.*?)"~', array($this, 'appendClasses'), $matches[0]);
    if (!$this->classes_found) {
      $classes = $this->classes;
      if ($this->count == 1) {
        $classes[] = 'first';
      }
      if ($this->count == $this->total) {
        $classes[] = 'last';
      }
      if (!empty($classes)) {
        $processed = rtrim($matches[0], '>') . ' class="' . implode(' ', $classes) . '">';
      }
    }
    return $processed;
  }

  private function appendClasses($matches) {
    array_shift($matches);
    list($name, $value) = $matches;
    if ($name == 'class') {
      $value = array_filter(explode(' ', $value));
      $value = array_merge($value, $this->classes);
      if ($this->count == 1) {
        $value[] = 'first';
      }
      if ($this->count == $this->total) {
        $value[] = 'last';
      }
      $value = implode(' ', $value);
      $this->classes_found = TRUE;
    }
    return sprintf('%s="%s"', $name, $value);
  }
}
0 голосов
/ 30 ноября 2008

Вы можете загрузить навигацию в SimpleXML объекте и работать с этим. Это предотвратит разрыв вашей разметки с помощью сумасшедшего регулярного выражения:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...