Модифицируем HTML на лету
Привет друзья. Хочу поделиться методикой, которую иногда использую в тех случаях, когда мне нужно подменить или каким то образом модифицировать на лету готовый HTML. Причем сделать это на уровне сервера, а не через JS.
Начнем с того, что определимся, каким образом вообще используя MODX и его технику сбора конечного кода страницы из множества разных частей (шаблонов, чанков, сниппетов), можно получить доступ к уже готовой конечной версии DOM дерева.
Для этого нам на помощь приходит событие для плагина OnWebPagePrerender — оно срабатывает именно в тот момент, когда веб страница собрана и готова к выводу. Единственное, что здесь не получится учесть — это код, выполняемый при помощи JS в момент рендера страницы браузером.
Исторически сложилось, что я для подобных задач использую библиотеку PHP Simple HTML DOM Parser, которая дает возможность перемещаться по DOM дереву и осуществлять перебор и подбор нужных элементов CSS или Jquery подобным способом.
В состав компонент входит свежая версия библиотеки и плагин-болванка.
Приведу еще несколько реальных примеров использования.
Очищаем контент от лишнего мусора в атрибутах
Некоторые примеры любезно предоставил @mngatoff в свое время, за что ему большое спасибо.
Более подробно о методах библиотеки можно почитать в официальной документации
Также использую эту библиотеку для парсера страниц. Если интересно — дайте знать, расскажу и покажу как нибудь.
Начнем с того, что определимся, каким образом вообще используя MODX и его технику сбора конечного кода страницы из множества разных частей (шаблонов, чанков, сниппетов), можно получить доступ к уже готовой конечной версии DOM дерева.
Для этого нам на помощь приходит событие для плагина OnWebPagePrerender — оно срабатывает именно в тот момент, когда веб страница собрана и готова к выводу. Единственное, что здесь не получится учесть — это код, выполняемый при помощи JS в момент рендера страницы браузером.
switch ($modx->event->name) {
case 'OnWebPagePrerender':
//Получаем доступ к готовому DOM дереву
$output = &$modx->resource->_output;
//Модифицируем как хотим
$output = preg_replace('|\s+|', ' ', $output);
break;
}Мы можем модифицировать HTML код нативными средствами PHP — для этого у нас есть preg_replace, str_replace и другие методы. Также мы можем использовать PHP Класс DOMDocument дающий более обширные возможности. Можем подключить и использовать и дополнительные библиотеки. Исторически сложилось, что я для подобных задач использую библиотеку PHP Simple HTML DOM Parser, которая дает возможность перемещаться по DOM дереву и осуществлять перебор и подбор нужных элементов CSS или Jquery подобным способом.
// Find all element which id=foo
$ret = $html->find('#foo');
// Find all element which class=foo
$ret = $html->find('.foo');
// Find all element has attribute id
$ret = $html->find('*[id]');
// Find all anchors and images
$ret = $html->find('a, img');
// Find all anchors and images with the "title" attribute
$ret = $html->find('a[title], img[title]');Немного модифицируем плагин и получаем вот такую историюswitch ($modx->event->name) {
// Стартуем плагин
case 'OnWebPagePrerender':
// Получаю доступ к DOM дереву
$output = &$modx->resource->_output;
// Здесь доступны все поля ресурса.
// Определяю с каким шаблоном имею дело.
$template = $modx->resource->template;
//Подключаю библиотеку
include_once MODX_CORE_PATH . 'components/simple_html_dom/vendor/simple_html_dom.php';
if (class_exists('simple_html_dom')) {
$html = new simple_html_dom;
$html->load($output);
switch ($template) {
case 17:
// Внутри конкретного шаблона Ищу все изображения внутри div.content
foreach ($html->find('.content img') as $img) {
/**
* @var simple_html_dom_node $img
*/
// Хочу активировать Lazy load для этого
// Сохраняю ссылку на изображение
$src = $img->src;
if ($src) {
// Удаляю атрибут src
$img->src = null;
// Добавляю атрибут data-src
$img->{'data-src'} = $src;
// Добавляю класс lazy
$img->class = 'lazy';
// Далее картинки с нужным классом и дата атрибутом подхватит js плагин lazy load и сайт прилично ускорит свою работу
}
}
break;
}
$output = $html->save();
$html->clear();
unset($html);
$output = preg_replace('|\s+|', ' ', $output);
}
break;
}Для удобства я упаковал всю описанную историю в компонент, который скоро будет доступен в наших репозитариях.В состав компонент входит свежая версия библиотеки и плагин-болванка.
Приведу еще несколько реальных примеров использования.
Очищаем контент от лишнего мусора в атрибутах
foreach ($html->find('.product_page table') as $table) {
$table->border = null;
$table->cellpadding = null;
$table->cellspacing = null;
$table->style = null;
$table->width = null;
$table->class = 'table table-responsive';
}
foreach ($html->find('.product_page table tr') as $tr) {
$tr->style = null;
}Добавляем обертку noindex для всех фреймовforeach ($html->find('iframe') as $iframe) {
$iframe->outertext = '<!--noindex-->' . $iframe->outertext . '<!--/noindex-->';
}Добавляем обертку noindex для внешних ссылокforeach ($html->find('link') as $link) {
if (is_external_url($link->href) && stripos($link->href, $site_url) !== 0) {
$link->outertext = '<!--noindex-->' . $link->outertext . '<!--/noindex-->';
}
}Добавляем rel=nofollow и target=_blank для внешних ссылокforeach ($html->find('link') as $link) {
if (is_external_url($link->href) && stripos($link->href, $site_url) !== 0) {
$link->rel = 'nofollow';
$link->target = '_blank';
}
}В общем как вы видите — довольно простыми манипуляциями можно как угодно модифицировать html разметку страницы без вмешательства в исходных код шаблонов и текста в текстовом редакторе. Некоторые примеры любезно предоставил @mngatoff в свое время, за что ему большое спасибо.
Более подробно о методах библиотеки можно почитать в официальной документации
Также использую эту библиотеку для парсера страниц. Если интересно — дайте знать, расскажу и покажу как нибудь.
Комментарии: 26
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
А то я делал подобное через него, но мне не понравилась производительность
Спасибо за статью
Чтоб не минифицировать докуметы формата txt, по типу robots.txt.
раз уж на то пошло, функции is_external_url в оригинале библиотеки нет, я ее сам туда дописал, вот она:
Или вот так можно fancybox подцепить:
То есть менеджер добавляет фото к статье как обычно, и может управлять его размерами. А плагин обрезает фото до заданных размеров и оборачивает его ссылкой, кликнув на которую откроется увеличенное изображение с помощью fancybox.
Но где то тут же я упоминал что гораздо более грамотно было бы делать это на событие сохранения ресурса, не гоже жертвовать скоростью отрисовки страницы ради служебных нужд. Возможно при одном посетителе это не заметно, но представь эти же самые операции когда одновременных посетителей 10 и более
amp — это у нас XML страница, генерируемая в MODX верно? А значит для нее так же можно включить данный плагин. Ну а плагину в общем то все равно что обрабатывать, HTML или XML.
А как переписать чтобы добавлял class lazy к существующим class'ам при наличии? и при отсутствии просто добавлял class=«lazy»?
Если нет — то сохрани в переменную сначала содержимое атрибута class, затем приконкатенируй свой класс и вставь в атрибут.
по сути не определяю в контенте это или нет, чтобы добавило класс.
Теперь работает так как надо. Спасибо
Но как оказалось она работает в том числе и для документов со всеми типами содержимого, в том числе text
Можно ли как-то ограничить действие только для типа содержимого HTML или исключить для text?
Пиши условие, проверяй у него content_type или как там, не помню сейчас на лету.