Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

Черновики у меня в комментариях

Мне тут хочется пару проектов на PHP доделать, а я что-то давненько, порядка полугода, на нём не программировал вообще. Всё больше Пайтон и Google Go. Поэтому я взялся внести несколько изменений в движок своего сайта, которые давно руки чесались сделать.

В том числе сделал то, что давно просили — черновики в комментариях. Работает только в современных браузерах — IE 8.0+, FF 3.5+, Safari 4.0+, Chrome 4.0+, Opera 10.5+, а так же iOS 2.0+ и Андроид 2.0+. Только такой список, так как сохранение происходит в localStorage.

localStorage — это клиентская база данных «ключ и значение», которые есть сейчас уже во всех распространённых браузерах. Преимущество хранения черновиков именно в ней — черновик сохраняется вне зависимости от того рухнул сервер или нет, ведь localStorage сохраняет данные на диске клиента.

Кстати, я делал библиотеку эмуляции localStorage для IE 6 и 7 (используется behavior userData), но у себя использовать не буду, ни к чему поощрять некрофилию.

Работает всё следующим образом (детально можно посмотреть в исходниках любой страницы с комментариями): когда пользователь делает паузу в наборе комментария, мой JS вызывает localStorage.setItem, где ключём является URL страницы плюс имя поля (у меня их три — имя пользователя, сам комментарий и кому этот комментарий был адресова).

После отправки комментария в теле полученной страницы приходят вызовы localStorage.removeItem, которые удаляют сохранённые значения.

Наконец, если страница была загружена не в результате отправки комментария, вызываются localStorage.getItem, чтобы получить сохранённые значения и подставить их в соответствующие поля.

Вообще-то к localStorage можно обращаться как к массиву, но моя библиотека эмуляции localStorage этот интерфейс не эмулирует. На случай, если я сменю гнев на милость и захочу поддержать IE 6 и 7, я решил пользоваться явными вызовами методов.

Код, с использованием JQuery, получился вот такой:

// globalStorage — это для FF 2.0, но я не уверен, что это правда работает
var storage = window.localStorage || window.globalStorage && window.globalStorage[location.hostname] || null;

if (storage) {
    var savedraft = function s() {
         if (s.hnd) {
             clearTimeout(s.hnd);
         }
        	
         var o = $(this);
        	
         s.hnd = setTimeout(function() {
             try { // если не хватит места, будет исключение
                 var prefix = location.pathname + '#';
                 storage.setItem(prefix + o.attr('name'), o.val());
                 storage.setItem(prefix + '?', +new Date()); // сохраняю время, возможно в будущем буду подчищать старые черновики
             } catch (o) {}
        }, 500);
    }
    
    // каждый элемент, который надо сохранять, имеет класс draft	
    $.each($('.draft'), function(_, value) {
        var o = $(value),
              key = location.pathname + '#' + o.attr('name'),
              v = storage.getItem(key);

    if (savedsuccessfully) { // внешний флаг, что черновик больше не нужен — всё сохранено на сервере		
                  storage.removeItem(key);
              } else if (v !== null) {
                  o.val(v);
              }
              o.keypress(savedraft).change(savedraft);
        })
    }

localStorage не резиновое, у разных браузеров значения различаются, но в целом, там около 5МБ на домен, да и диск пользователя забивать жалко. Поэтому я сохраняю время записи, возможно когда-нибудь буду вычищать очень уж старые черновики.

21 комментарий
Рамиль К (ramil1017.ya.ru) 2011

Если предположить, что сервер может лежать, то было бы клёво ещё записывать при сабмите, а удалять только после положительного ответа сервера.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для ramil1017.ya.ru:

Если предположить, что сервер может лежать, то было бы клёво ещё записывать при сабмите

Да, так и надо сделать.

а удалять только после положительного ответа сервера.

Только так и происходит.

Евгений Степанищев (bolknote.ru) 2011

Правда, похоже, ещё наблюдаются глюки. Надо программировать на свежую голову.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для ramil1017.ya.ru:

Глюки поправил и сделал сохранение перед сабмитом.

Сергей Чикуёнок (chikuyonok.ru) 2011

Комментарий для Евгения Степанищева:

Я бы предложил вот так хранить (удобнее для работы):

localStorage.setItem(’bolkComment’, JSON.stringify({
    draft1: {
        content: o.val(),
        date: new Date()
    }
}));

Евгений Степанищев (bolknote.ru) 2011

Комментарий для chikuyonok.ru:

Мне ночью лениво было смотреть поддерживается ли нативный JSON (а библиотеку тянуть не хочется) в тех же браузерах, где есть localStorage/globalStorage. Подозреваю, что в FF 2.0 этого нет, но его поддержку (фактически в коде она есть) можно и убрать.

PastorGL.ya.ru 2011

Похоже, что в IE9 localStorage именно что резиновое.

При первой попытке его забить удаётся записать ~5MБ, после очистки при повторной попытке уже ~7.5МБ на тот же домен, затем ~20 и ~99, а дальше скрипт может неожиданно упасть с ошибкой типа «память кончилась». Любопытно.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для PastorGL.ya.ru:

Хм… :) Забавно.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для chikuyonok.ru:

JSON.stringify поддерживают ( http://robertnyman.com/javascript/javascript-native-json.html ):

IE8, FF3.5, Опера 10.5, Google Chrome 4.0+; Safari 4.0+.

Гм. Выглядит подходяще под список localStorage. Осталось узнать как дела у мобильных браузеров обстоят.

Vladimir Moskva (fulc.ru) 2011

Опера при переполнении стораджа только что предложила увеличить лимит для текущего сайта.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для fulc.ru:

Когда у тебя хранилище могло заполниться уже? А посмотри что у тебя там?

boltai-shaltai 2011

Комментарий для Евгения Степанищева:

Кстати, я делал библиотеку эмуляции localStorage для IE 6 и 7

А с какой целью? Как упражнение или была какая-то надобность?
Спрашиваю потому, что эту фишку пора уже активно использовать. Нужны всяческие идеи и примеры, а то свою голову клинит консерватизмом. Столько времени изгонять мысли о персистенстности данных на клиенте, и внезапно осознать, что уже приплыли, что применять можно и нужно.

boltai-shaltai 2011

Комментарий для fulc.ru:

Опера при переполнении стораджа только что предложила увеличить лимит для текущего сайта

Как оно выглядело, не помните? Внятно, не пугающе?

Непонятное сообщение может не только домохозяйку отпугнуть от сайта. Типа такого: «Сообщение модуля безопасности: этот сайт запрашивает увеличение квоты жёсткого диска! Рразрешить/Запрретить?!». Брр.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для boltai-shaltai:

А с какой целью? Как упражнение или была какая-то надобность?

Черновики, которые я тут сделал, я несколько месяцев назад реализовал в нашей внутренней Вики. Тогда мы ещё считали, что IE7 надо поддерживать.

Назар 2011

Подкажите, а что дает «|| null» в цепочки логических вычислений (первая строка)?

Евгений Степанищев (bolknote.ru) 2011

Комментарий для Назар:

Да ничего особенного. Просто переменная становится null, а не undefined.

Orcinus Orca (orcinus.ru) 2011

Шикарно, у меня уже с этого сайта 56 записей в сторадже. Кстати, можно посмотреть их количество написав в строке браузера javascript:localStorage.length; а удалять их можно там же в строке браузера: javascript:localStorage.clear();

Orcinus Orca (orcinus.ru) 2011

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

Евгений Степанищев (bolknote.ru) 2011

Комментарий для orcinus.ru:

Зачем? При существующих объёмах жёстких дисков эти 56 записей — копейки.

Orcinus Orca (orcinus.ru) 2011

Комментарий для Евгения Степанищева:

В принципе — логично. Но у некоторых браузеров всё-таки есть предел по объему. Я сейчас на своём сайте экспериментирую с локалстораджем и у меня несколько сотен записей. Но ведь это всё-равно не будет продолжаться вечность. Хочу ещё использовать его для хранения черновиков на корпоративном сайте, так, что там можно будет развернуться и проэкспериментировать на полную мощность.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для orcinus.ru:

На всех браузерах он есть, закреплён в стандарте — 10МБ.