jQuery

индекс
283,92

Фильтруем с помощью jQuery большое количество данных

image У меня на хабре очень крупное приданное избранное. Там что-то найти было очень сложно, а если ещё не помнишь как и что, то чрез чур. По сему решил написать на jQuery фильтр всего этого добра по тэгам. Для того бы сграбить закладки, прошлой весной ukko сделал простой граббер. В итоге у нас получался огромный htm в формате NETSCAPE Bookmarks и xml.

Принцип работы прост, устанавливаем себе грабер, получаем заветный habrabookmarks.htm, и просто запускаем мой файл сортировки рядом.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
 <title>Sort Bookmarks</title>
 <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.js"></script>
<script type="text/javascript">
// делаем массив из уникальных значений
getUniqueValues = function (arr) {
  var hash = new Object();
  for (j = 0; j < arr.length; j++) {hash[arr[j]] = true}
  var array = new Array();
  for (value in hash) {array.push(value)};
  return array;
}

// аналог php функции
function nl2br (str, is_xhtml) {
  var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
  return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
}

// кастомные функции для проведения сортировок списка.

/*
* Фильтр тэгов по 1 символу
* Нам нужна только одна буква strg (String)
*/

function _sort (strg) {
  // покажем ссылку сброса фильтра
  $(".sat").show();
  // спрячем все тэги и запятые после них
  $("#tagz .tgs").hide().next().hide();
  // покажем тэги которые ничинаются на strg, и запятые после них
  $("#tagz .tgs[rel^="+strg+"]").show().next().show();
  return false;
}

/*
* Сброс фильтра тэгов
*/

function C_sort() {$(".sat").hide();$("#tagz .tgs").show().next().show();return false;}

/*
* Фильтр всех записей
* Нам нужен тег целиком strg (String)
*/

function _filter (strg) {
  // покажем ссылку сброса фильтра
  $(".sabm").show();
  // спрячем все записи и их описания
  $("DL A").parent().hide().next().hide();
  // покажем записи с тэгом strg, и их описание
  $("DL A[tags*="+strg+"]").parent().show().next().show();
  return false;
}

/*
* Сброс фильтра записей
*/

function C_filter () {$(".sabm").hide();$("DL A").parent().show().next().show();return false;}

// Обрабатываем файл к тором хранятся наши закладки
$.get('habrabookmarks.htm', function(data) {
  $('body').html(data);

// Создаём див для тэгов и фильтра тэгов, и помещаем его перед заголовком
$("<div>")
    .attr("id","tagz")
    .css({
      "float":"right",
      "padding":"10px",
      "width":"310px",
      "height":"auto",
      "overflow":"auto"
      })
    .insertBefore($("h1:eq(0)"));
    
  // Вставляем кнопку сброса фильтра записей
  $("h1:eq(0)").append(" <a href='javascript:' class='sabm' style='display: none; color: green;' onclick='C_filter();'><small>(show all)</small></a>");
  
  // Переменные для работы
  var
    allTags = aT = curSA = [],
    abc = ".-1234567890qwertyuiopasdfghjklzxcvbnmйцукенгшщзхфвапролджэячсмитбю",
    j = 0;
  
  // делим строку для фильтра тэгов на масив и сортируем
  abc = abc.split('').sort();
  // выводим в виде ссылок
  for (var i in abc)
    curSA[i] = "<a href='javascript:' onclick='_sort($(this).text())'>"+abc[i]+"</a>";
  // добавляем в начало нашего дива + ссылка сброса фильтра тэгов
  $("#tagz").append(curSA.join(", ") + " <a href='javascript:' class='sat' style='display: none; color: red;' onclick='C_sort();'>x</a> <br><br> ");

  // выбираем все тэги из записей
  // тэги хранятся в ссылках в параметре tags
  $("DL A").each(function(){
    // получаем значение атрибута и превращаем в масив с тегами
    allTags = $(this).attr("tags").split(',');
    // дальше получаем описание заметки
    var curTDA = $(this).parent().next();
    // преобразовываем его
    curTDA.html(nl2br(curTDA.html()));
    // если тэгов не ноль, обрабатываем их
    if (allTags.length > 0) {
      var curA = [];
      for (var i in allTags) {
        // один массив для всех-всех тэгов, другой только для тэгов данной заметки
        aT[j] = curA[i] = "<a class='tgs' rel='"+allTags[i]+"' href='javascript:' onclick='_filter($(this).text())'>"+allTags[i]+"</a>"
        j++;
      }
      // добавляем тэги(через запятую) к заметке
      curTDA.append("<br>" + curA.join(', ') + "<hr>");
    }
  }).attr("target","_blank"); // вконце просто пускае ссылки в новом окне
  // добавялем в наш див все уникальные тэги, отсортированные и разделённые запятыми
  $("#tagz").append(getUniqueValues(aT).sort().join('<span>, </span>'));
// Сказочке конец, а кто слушал молодец
});

</script>
</head>
<body>
</body>
</html>




Демо и исходный файл закладок
Архив
+13
18 мая 2010, 16:53
55

комментарии (38)

+1
Voenniy #
«Демо» подвесило мой firefox — так и должно быть?
НЛО прилетело и опубликовало эту надпись здесь
+23
homm #
Ах, вот оно что :)
Небыстро фильтруем с помощью jQuery большое количество данных.
Я уж думал, что-то интересное.
–4
deerua #
там сам ДОМ долго думает, он рядом
–11
deerua #
homm, ты вот такой трольгений,
мне просто интересно, как ты быстро это реализуешь, :)
Спасибо!
+34
homm #
Ну, давай попробуем.
Тормозит обработчик $.get('habrabookmarks.htm'); Общее время выполнение на моей системе в моем браузере (Опера 10.53) — 6530мс.

Обработчик можно разделить на 3 части:
1) то, что до $(«DL A»).each(); — 33мс
2) то, что внутри $(«DL A»).each(); — 2070мс
3) одна строчка после — 4426мс.

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

Строчка curTDA.html(nl2br(curTDA.html())); выполняется 450мс. Как ни странно главный тормоз — функция nl2br, а не работа с домом. Вынесение создания регулярного выражения за пределы функции ускоряет всю строку до 190мс.

Остальной код в этой части усиленно что-то аппендит в curTDA. Помня о тормознутости метода append, заменяем его на быстрый html(). Благо, что все содержимое этого метода формируется тут же: pastie.org/966270

Теперь вторая часть выполняется за 330 мс, дальше семечки.

Следующая строчка выполняется 4426мс:
$("#tagz").append(getUniqueValues(aT).sort().join('<span>, </span>'));

Я уже говорил, что append — медленная функция? Заменяем:
$("#tagz").html($("#tagz").html() + getUniqueValues(aT).sort().join('<span>, </span>'));

Получаем 260мс. Почти в 20 раз. Но это не все, getUniqueValues(aT) — не нужен. Мы можем с самого начала хранить только уникальные значения тегов в хеш-объекте. Если это реализовать, получится 75 мс на последнюю часть.

Итого — 33 + 330 + 75 = 438мс. Это более чем в 10 раз быстрее первоначального результата. Полный код здесь: pastie.org/966296 Уж извини, в таком же неопрятном состоянии, как был до меня.

Вот так бы я это реализовал, deerua, вот так.
+11
pepelsbey #
Мужик!
+8
mktums #
Who&#39;s awesome? You&#39;re awesome!
–3
rnbparty #
не ну да, конечно, homm я за тебя, но эксперимент не чистый. сколько времени ты потратил на разработку кода? между камментами 6 часов.
каких то 6 часов и поиск по закладкам не 6 секунд, а 0.6. по-моему результат результатов :))
+1
mktums #
Победителей не судят.
К тому же целью была не скорость проведения соревнования, а грамотная реализация функционала.
+6
homm #
Потратил минут 15, не больше. Больше ушло на комментарий.
+2
xiaose #
Мало того, что хорошо ответил так и не поленился же написать столько… молодец! Реально молодец.
0
deerua #
о, спасибо!
а.) файл habrabookmarks.htm около мегабайта, у большинства именно из-за этого проблемы, проверил на медленных интернетах. Хотя фокс не падал, просто долго думал
б.) curTDA.html(nl2br(curTDA.html())); — отключена в демо изначально (закомментирована)
в. ) знал что html быстрее, но не думал что на столько :)

зы: ещё раз спасибо, внедрю сразу эти правки ;)
удачи!
+2
homm #
файл habrabookmarks.htm около мегабайта
На самом деле 270кб. G-zip, все дела.
nl2br отключена в демо изначально
Когда я первый раз смотрел вроде, была включена. А вот когда писал комментарий, отключена.
0
deerua #
да, упустил гзип
«оптимизация» была произведена почти сразу, после запуска статьи
0
deerua #
Демо на ~1000 записей.
Тестировал на ФФ 3.6.4 и Хромыга 5.ъ
Всё довольно быстро было
0
Voenniy #
Странно, у меня довольно быстрый комп — однако firefox реально подвисал на несколько минут.

+1
deerua #
Максимум 3 секунды, комп простой, не сказал бы что быстрый.
в ИЕ даже тестировать не хочу, остального нет.
0
Voenniy #
Странно. У меня подвисает довольно сильно, после чего выводится окошко с предложением остановить сценарий. FF 3.6.3
А вот в хроме — действительно быстро.
+2
Denai #
странно, но всё стработало за секунду… наврено время в моём ff ускорилось в момент нажатия на ссылку..(
НЛО прилетело и опубликовало эту надпись здесь
+3
valCooL #
Вее почти моментом отрабатывает в ласт опере.
+2
hshhhhh #
Ну вот, моё избранное стало ещё больше.
+2
anmiles #
Бытует примета (правда не всегда исполняется), что через некоторое время после того как ты сделаешь крутой парсер с клиенсткий прибамбасами для своего любимого ресурса, разработчики этого ресурса делают фичу для этих же целей и твой парсер становится уже не нужен :)
0
vslobodyan #
«делают фичу» -> «копируют фичу»?
0
deerua #
Было бы супер, но избранное у некоторых очень толстое и делать что-то подобное ресурсоёмкое, я думаю это не о хабре.
Может что-то подобное но не совсем в такой реализации.
+1
Thomas #
Делал как-то что-то подобное для отображения моей коллекции музыки. Хочу сказать что задачу надо было решать не только при помощи jQuery а еще позвать на помощь XSLT. Браузеры умеют обрабатывать XSLT к тому же там возможно сделать и группировку и сортировку.
0
demogorgorn #
где можно взять граббер ибо ссылки ни из этого поста, ни из поста автора не работают?
+1
deerua #
всё вроде ок
code.google.com/p/habrabookmarks/
join
–2
highw #
Повесил мне ФФ, спасибо

Крутой скрипт, ага…
+1
deerua #
Писал его дома на доходяге ноутбуке, ни разу не повис.
Проблемы, как вариант, в засраности фокса :) Или он просто старый, переходите на 3.6
0
highw #
Конечно засранный, 3.6.3, ставил неделю назад…
0
TheShock #
а файрбаг есть? включен?
–2
Panya #
Ты бы еще пару томиков «Война и мир» загрузил и распарсил по буквам и словам с аппендом сгенеренных ссылок. Фильтруем, ну-ну.
0
deerua #
Я загрузил все свои закладки, это была моя цель :)
0
Panya #
Причем тут сколько у тебя закладок? Разве не очевидно, что такие операции (генерация ссылок для фильтрации контента) лучше производить на сервере? Если посмотреть на результаты профайлинга твоего скрипта, то сразу видно, что львиную долю времени занимает именно генерация ссылок и их вставка в документ. Зачем вообще это делать на клиенте? Я имею ввиду не саму фильтрацию а именно загрузку и парсинг html с целью разбить его на заведомо известные куски для их вставки?
–1
Dr_Sta #
Версия для GoogleChrome планируется?
+1
Lord_Daedra #
даю идею: сделать базу CouchDB и туда записывать данные, взаимодействие с ней через JSON, то есть довольно легко можно переделать эти скрипты, jQuery посылает запрос и получает нужные данные… ну и, думаю, производительность вполне заметно возрастёт за счёт этого :-)

самые догадливые сделают из этого стартап — сервис, где будет возможность простой регистрации и граббинг закладок хабра, типа если меньше 1000 записей в избранном, то сервис бесплатный, если меньше 10 000 записей то 100 рублей/месяц и так далее… (цифры с потолка)…

куча всяких полезных и бесполезных идей)) кто бы только всё это реализовал))

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.