Таблица mysql загружается в браузер; при Ctrl+Alt+click ячейка становится редактируемой; по Ctrl+Enter текст ячейки сохраняется в базу без перезагрузки страницы. Без Ajax, без jQuery!

Редактирование таблицы MySQL: Javascript интерфейс

В предыдущей статье мы рассмотрели предельно простой способ доступа к базе данных MySQL (редактирование таблицы без перезагрузки HTML страницы). Отправка данных на сервер производилась через статический элемент iframe, помещаемый программой рядом с тем местом, куда пользователь щёлкнул мышкой (рядом с редактируемой ячейкой).

Статический iframe был выбран нами в качестве транспорта для того, чтобы пользователь мог видеть реакцию сервера напрямую (удалось ли изменить данные MySQL, или HTTP-сервер вернул ошибку, и какую именно). Программа получилась действительно простой и прозрачной для пользователя. Но простота повлекла за собой и неизбежные недостатки (или просто неосуществлённые возможности):

  1. История (журнал пользователя в браузере) засоряется «служебными» (невидимыми) обращениями к серверу.
  2. При перезагрузке страницы браузер задаёт вопрос о повторной отправке данных POST-формы.
  3. Более естественным редактирование выглядело бы, если бы поле textarea возникало прямо на месте выбранной пользователем ячейки (а не рядом).
  4. На освободившемся месте (рядом с ячейкой) можно отображать историю изменений текста данной ячейки.

Нетрудно видеть, что последние три пункта к способу транспорта прямого отношения не имеют, однако пресловутое засорение истории (о котором так много говорят любители Ajax'a) всё-таки заставляет делать iframe динамическим. Это значит, мы теряем «естественные» ответы сервера, и сообщать пользователю об успехе (или провале) операции придётся с помощью Javascript.

Рабочий пример получившейся системы можно протестировать на странице http://ir2.ru/static/tableedit2.htm. Функции Javascript, необходимые для работы, разместились в трёх файлах: http://ir2.ru/static/ir2.js (3.5K, общая библиотека), http://ir2.ru/static/tabsort.js (3.5K, сортировщик HTML-таблицы, необязательный элемент), http://ir2.ru/static/tableedit2.js (5.5K, редактор таблицы). На сервере данные, полученные от пользователя, обрабатывает небольшой (1.5K) скрипт http://ir2.ru/static/savecell2.php.

Принципиальная схема табличного редактора

Страница HTML с таблицей для редактирования:

  1. HTML таблица с данными MySQL рисуется по определённым правилам: а) class таблицы должен быть 'sortable'; б) в первой строке должны быть наименования полей MySQL; в) в первом столбце должны быть уникальные идентификаторы строк MySQL. Всё это без лишних тэгов, голая структура и содержание.
  2. В Javascript (при формировании страницы на сервере) передаётся список разрешённых для редактирования полей (переменная validfield). Это не обязательно, но в каких-то случаях (например, в нашем учебном примере) может пригодиться.
  3. На страницу добавляется два пустых (невидимых) контейнера: div id='framebox' – для динамически создаваемого iframe, div id='divhist' – для сохранения версий редактируемых ячеек.
  4. Добавляется форма (невидимая) с основным полем textarea для редактируемых текстов и вспомогательными полями-идентификаторами строк и полей таблицы MySQL.

Вся остальная работа делается Javascript:

  1. При щелчке (Ctrl+Alt+Click) по ячейке, которую разрешено редактировать, ячейка как бы превращается в поле textarea: поле в форме для редактирования делается видимым и совмещается по координатам top-left (и по размерам) с выбранной ячейкой.
  2. В поле textarea передаётся текст (innerHTML) редактируемой ячейки.
  3. Если в этом сеансе работы ячейку уже редактировали, ниже ячейки выводится история изменений.
  4. Для сохранения изменённого в ячейке (в форме) текста надо произвести то же действие, что и для начала редактирования: Ctrl+Alt+Click. Или Ctrl+Enter. По этому действию пользователя создаётся временный элемент iframe, и через него форма отправляет данные на сервер.
  5. Для отказа от редактирования нужно нажать Esc – тогда и текстовое поле, и история изменений исчезнут.
  6. Если страница, открываемая во временном iframe, недоступна, iframe делается видимым пользователю до тех пор, пока не выскочит сообщение об ошибке связи. Это в ИЕ, который не позволяет получать текст страницы с ошибкой "404". В нормальных браузерах Javascript нормально забирает текст страницы с ошибкой и передаёт в историю изменений ячейки.
  7. Если со страницей в iframe всё в порядке, она после обработки данных MySQL возвращает Javascript код, который сообщает главной странице о результатах операции по сохранению данных.
  8. В случае успеха (строка таблицы MySQL была изменена) форма редактирования и контейнер истории исчезают. В случае проблемы в историю изменений добавляется пункт с сообщением "Таблица MySQL не изменилась" и с текстом sql-запроса.
  9. Добавление новой записи: в HTML таблицу подрисовывается (вверху) строка без идентификатора; тексты ячеек в ней выглядят как наименование поля с добавлением "_новое". При попытке редактировать новую строку ей присваивается идентификатор (реальный id вставленной новой записи в таблицу MySQL), а в HTML таблицу подрисовывается ещё новая строка без идентификатора.
  10. Строки для добавления новой записи подрисовываются всем HTML таблицам с classname='sortable'. В данной версии всё равно можно корректно редактировать только одну таблицу – у которой заголовки и идентификаторы совпадают с соответствующими постоянными параметрами MySQL в скрипте обработки формы savecell2.php. Но в следующей версии Simple HTML Table Editor можно будет редактировать несколько разных таблиц (точнее, любую произвольно выбранную таблицу MySQL).

Некоторые особенности манипуляций с DOM

Самая сложная задача – выловить из временного элемента iframe ответ сервера. Она возникает как следствие из нашей генеральной установки считать Ajax лишней сущностью. Сложной является только часть задачи: при нормальных ответах сервера iframe получает нормально сгенерированную страницу, на которой запускается Javascript, передающий родительскому окну необходимую информацию – ничего сложного, всё стандартно!

А вот если, например, страница для iframe (savecell2.php) недоступна, сообщить об этом пользователю изнутри iframe не получится; надо будет из главного окна получить содержимое документа iframe. Тут тоже годится "штатная", отработанная нами в предыдущей версии Table Editor технология:

Проблема возникает даже не на уровне манипуляций с DOM, а на уровне идеологии: это хорошо известное уродство ИЕ – при получении ошибки сервера "404" выдавать свою собственную страницу ошибки (и вот к этой-то "своей" странице ИЕ не предоставляет программного доступа).

Из-за этого (весьма характерного для Микрософт) скотства как раз и возникает небольшая сложность, которую мы обходим с помощью таймера:

  1. при отправке формы на сервер включается таймер в одну секунду;
  2. если страница savecell2.php успевает вернуть данные (а она очень маленькая, и за секунду в норме успеет загрузиться раз двадцать), Javascript из фрейма выключает таймер и дальше всё работает как обычно;
  3. если таймер срабатывает, Javascript главного окна извлекает текст страницы из фрейма и выводит этот текст в истории изменений редактируемой ячейки;
  4. если таймер сработал, но при извлечении текста страницы фрейма Javascript получил фигу (null), значит, это пытающийся всеми управлять Микрософт;
  5. тогда мы сами пишем в историю ячейки "Ошибка связи"; iframe остаётся видимым, пока пользователь не закроет выскочившее сообщение (alert) с текстом ошибки (e.message), возникшей при попытке доступа к фрейму; с учётом этих поправок код обработки ошибок несколько усложнится:

Философия

Хотя это наоборот – скорее, логика. Программа-редактор таблицы начинает работу по определённому действию пользователя – щелчку мышкой с удержанными клавишами Контрол и Альт. К чему должно быть привязано это событие? К какому объекту или элементу HTML?

Возможны два варианта:

1) привязать это событие к каждому элементу, который можно редактировать – с помощью функции, которая должна будет как-то обнаружить все такие элементы на странице;

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

Мы не рискнули (или поленились) заранее попытаться определить, куда пользователю можно щёлкать, а куда нет, и выбрали второй путь. То есть программа обработки щелчка запускается, куда бы пользователь ни щёлкнул. А значит, надо вовремя программу останавливать. Это делается на трёх разных этапах работы:

  1. В самом начале, при поиске элемента td, внутри которого был щелчок: (если среди «предков» щёлкнутого элемента нет td, или у найденного td нет parentNode, программа прекращает работу).
  2. При поиске наименования поля MySQL в первой строке таблицы (headers – список ячеек первой строки):
  3. При отсутствии наименования поля MySQL в списке разрешённых для редактирования (validfield – список разрешённых полей):

Последний камень

При использовании Ajax (xmlHTTPRequest) для отлавливания ошибок (и вообще сообщений) сервера существуют специальные методы, призванные вроде бы облегчить работу. Они и облегчают – на ошибке "404" ("Страница не найдена"), которую мы выбрали для тестирования как самую простую. Но в реальной жизни практически невероятно встретить такую ошибку в работе фоновых HTTP-запросов: организуя отправку через Javascript таких запросов, мы ведь сами указываем страницу-обработчик, которую сами же и помещаем на сервер.

Гораздо более вероятно примитивнейшее "зависание" HTTP-сервера или "тормоз" канала связи (или ещё какие-нибудь проблемы интернет-провайдера) – что во всех случаях будет выглядеть просто как отсутствие какого-либо ответа. И тогда "штатный" Ajax теряет все свои преимущества перед нашей простенькой конструкцией с iframe: и там, и тут проблема решается одинаково – с помощью таймера. У нас она в случае любых ошибок (т.е. при отсутствии HTTP-ответа "200 OK" в течение секунды) решается с помощью таймера, т.е. более единообразно и просто, чем в Ajax.

Ну, и ничего страшного не случится, если таймер сообщит пользователю об ошибке связи, а после этого отработает серверный скрипт и пришлёт сообщение "OK" – работа будет сделана, и пользователь узнает об этом.

D.M., admin

Комментарии

des 27.01.2015 23:05:26

http://dn.ir2.ru/ct/mmedit/ не открывается. скиньте исходники на облако какое нить пжст

лесник 08.08.2012 17:49:27

GТ,

Эта статья описывает игрушку – заготовку к mysql-редактору (реализацию можно посмотреть на стр. http://dn.ir2.ru/ct/mmedit/). В рабочей версии тоже нет кнопки "удалить запись" :-).

Она ведь рассчитана на профессиональное использование, и мне совсем нетрудно ввести в поле запроса что-нибудь вроде "delete from `cache` where id=1".

Для заказчика, который умеет только править тексты, такие редакторы не годятся, там надо делать надёжные, кондовые формы редактирования, с валидацией, проверкой целостности данных, ну, и со ссылкой "Удалить", если надо...

04.08.2012 00:34:47

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

лесник 15.08.2011 20:57:07

Aden, там (ни в config.php, ни в configbase.php) НИГДЕ нет подключения к БД, так что исходные коды этих файлов вам не помогут, а скорее запутают (там не такой код, который можно всем показывать в качестве образца). На этом конкретном сайте авторизация происходит через php.ini.

Вам же лучше в своём коде использовать стандартную функцию php mysql_connect("localhost", "mysql_user", "mysql_password") с параметрами вашего сайта.

Aden 12.08.2011 23:31:05

Использовал исходники. Не работает. Где в configbase нужно прописать подключение к базе? Можете выложить исходники программы?

лесник 03.02.2011 20:49:10

Исходный код учебной таблицы: http://ir2.ru/static/tableedit2.zip

лесник 05.05.2010 23:04:33

Всё, исправил. Сафари тут ни при чём. Просто поменял систему скрытия объектов (сделал на classname вместо display:none) – в одном месте поменял, в другом не поменял, вот они и перестали скрываться.

А вообще это уже вчерашний день. Новейшая разработка (mysql-редактор): http://dn.ir2.ru/mmedit/

лесник 05.05.2010 22:25:34

Ну, хорошо, что вообще комментарии отправляются. Самый-то плохой javascript при отправке происходит. Пойду смотреть, почему esc не работает. А вообще, не люблю я Сафари...

Аркадий 29.04.2010 01:33:38

и когда тут у Вас оставляешь комент, непонятно куда он девается....добавился или нет 0_0

Аркадий 29.04.2010 01:25:34

У меня тут вопрос такой, у меня операционна Макинтош на ноуте аппловскойм. Браузер сафари. Так вот, когда нажимаю редактировать все ок, но не могу выйти из редактирования! Т/е/ жму esc и ничего не происходит, сохраняет нормально но опят же не выходит из режима редактирования