Встроенная система редактирования

Не стоит заставлять пользователей кликать по множеству страниц, чтобы отредактировать одно-единственное поле – это несовременно. В этом уроке вы узнаете, как создать встроенную систему редактирования – подобными системами оборудованы многие популярные сайты, например, Flickr.

От автора

Вся эта суматоха вокруг Web 2.0 лишний раз доказывает, что простота использования программного продукта сегодня крайне важна. Многие пользователи хотят иметь возможность редактировать содержание веб-страницы без необходимости переходить на другую страницу. Если вы бывали на сайте Flickr, то наверняка понимаете, о чем я говорю.
Впрочем, лучше один раз увидеть, чем 100 раз услышать, так что запускайте демо-версию и смотрите.

Сегодня мы с вами научимся создавать нечто подобное с помощью – да, вы угадали! – нашего любимого фреймворка jQuery! Заинтересовались? Тогда вперед!

Определимся с целями

До того, как мы начнем заниматься функционалом, следует определиться с целями нашей работы и ее предполагаемым конечным результатом.

• Прежде всего, пользователь должен иметь возможность редактировать содержание без перехода на другую страницу. Это наше исходное задание.
• Написанная программа должна либо функционировать безупречно, либо вообще не работать. Недоработки и ошибки в JS-программе исправить крайне сложно.
• Пользователь должен знать о том, что содержание сайта можно редактировать – пусть ему указывает на это бледно-голубой фон текста.
• Существует несколько способов организации перехода в режим редактирования: можно использовать либо одинарный, либо двойной щелчок левой кнопкой мыши. Я выбрал двойной щелчок, так как вероятность того, что пользователь случайно нажмет на текст два раза, гораздо меньше, чем вероятность того, что он случайно кликнет по тексту один раз. В случае необходимости эту настройку можно будет легко поменять в коде программы.
• У пользователя должна быть возможность сохранения отредактированного текста либо отмены редактирования.
• Сохранение и редактирование можно организовать двумя способами: клавишными комбинациями либо нажатием кнопок мыши. Я предпочел мышь, так как большинство клавишных комбинаций уже заняты.
• Кроме кнопок мыши, можно использовать стандартные кнопки либо ссылки. Я выбираю ссылки – просто потому, что они мне больше нравятся.
• Редактирование должно автоматически возобновляться в случае, если пользователь случайно нажмет на область за пределами поля ввода текста или же закроет страницу, а затем решит вернуться.
• Кроме того, пользователю должно быть доступно одновременное редактирование максимально возможного числа элементов.

Итак, теперь мы определились с целями и можем приступать к разработке плана по их достижению.

План действий

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

Шаг 1. Следует отнести к классу «редактируемые» все элементы, которые мы собираемся сделать таковыми.
Шаг 2. Далее мы делаем так, чтобы редактируемые элементы меняли фон при наведении на них курсора. Выделение будет появляться и исчезать за счет JavaScript вместо CSS: так как не все устройства и браузеры, воспринимат JavaScript – не хотим же мы, чтобы пользователи видели совсем не то, что мы предполагали.
Шаг 3. Мы должны сделать так, чтобы после двойного щелчка мыши по тексту, который может быть изменен, этот текст заменялся окном редактирования, в котором будет представлен старый текст.
Шаг 4а. Следует дать пользователю возможность сохранить отредактированный текст, то есть записать его в исходный элемент, а затем закрыть окно редактирования.
Шаг 4б. На случай, если пользователь хочет отменить редактирование, следует предоставить ему возможность вернуть прежнее содержание и закрыть окно редактирования.

Вот что нам предстоит сделать в процессе написания функционала. Конечно, есть некоторые подпункты, но я объясню их по ходу дела.

Разметка основной части

HTML-код разметки демо-версии выглядит примерно так:
<!DOCTYPE html>
  <html lang="en-GB">
  <head>
  <title>In-place editing system - by Siddharth for NetTuts</title>
  <link type="text/css" href="css/style.css" rel="stylesheet" />
  <script type="text/javascript" src="js/jquery.js"></script>
  <script type="text/javascript" src="js/mojo.js"></script>
  </head>

  <body>
<div id="container">
<h1>In-place editing</h1>
  <div>by Siddharth for the lovely folks at Net Tuts</div>
  <p>Elements with a class of <em>editable</em> are, well, editable. In case you haven't noticed, all 
  elements containing the <em>editable</em> class get a blue background on hover to indicate this capability. </p> 
<p>Double click to edit the contents. Use the dynamically created links to save or discard the changes. 
  You can open up as many fields to edit as you want without any hiccups.</p>
<div class="block">
  <h2>I </h2>
  <ul>
  <li class="editable">am Siddharth</li>
  <li class="editable">love working with the web</li>
  <li class="editable">am a freelancer</li>
  <li class="editable">write for Net Tuts</li>
  <li class="editable">can be found at <a href="http://www.ssiddharth.com">www.ssiddharth.com</a></li>
  <li class="editable">will never let you down or give you up :)</li>
  </ul>
  </div>
<div class="block">
  <h2>Things to do this week</h2>
  <ul>
  <li class="editable">Get design approval from Deacon</li>
  <li class="editable">Send an invoice to Albert </li>
  <li class="editable">Start work on Dwight's project</li>
  <li class="editable">Talk with Sarah about new ideas</li>
  <li class="editable">Check Seth's site for rendering bugs</li>
  <li class="editable">Meet with Clintson to discuss project</li>
  </ul>
  </div>
</div>
  </body>
  </html>

Как видите, если не принимать во внимание так называемый бойлер-плейт (повторение синтаксических шаблонов), у нас есть два списка, которые необходимо упорядочить: каждый элемент li относится к классу «редактируемые», и нам следует показать пользователю, что он может корректировать эти элементы веб-страницы.
Также мы использовали библиотеку jQuery и наш собственный скрипт.

CSS
body{
font-family: "Lucida Grande", "Verdana", sans-serif;
font-size: 12px;
}
a{
  color: #000;
  }
a:hover{
  text-decoration: none;
  }
p{
  margin: 30px 0 10px 0;
  }
h1{
  font-size: 30px;
  padding: 0;
  margin: 0;

  }
h2{
  font-size: 20px;
  }
#container{
  width: 820px;
  margin-left: auto;
  margin-right: auto;
  padding: 50px 0 0 0;
}
.editHover{
  background-color: #E8F3FF;
  }
.editBox{
  width: 326px;
  min-height: 20px;
  padding: 10px 15px;
  background-color: #fff;
  border: 2px solid #E8F3FF;
  }
ul{
  list-style: none;
  }
li{
  width: 330px;
  min-height: 20px;

  padding: 10px 15px;
  margin: 5px;
  }
li.noPad{
  padding: 0;
  width: 360px;
  }
form{
  width: 100%;
  }
.btnSave, .btnCancel{
  padding: 6px 30px 6px 75px;
  }
.block{
  float: left;
  margin: 20px 0;
  }

Ничего особенного – просто несколько html - кодов для разметки и стиля.
Обратите внимание на функции editHover и noPad – мы про них еще вспомним.

JavaScript

Итак, теперь у нас есть основа функционала, смоделированная некоторым образом – можно начинать действия по приведению функционала в соответствие с нашими требованиями. Не забывайте, что мы будем использовать jQuery, причем потребуется версия не ниже 1.3 – все остальное не будет работать.
Добавление псевдо-класса hover для изменения фона

Как мы уже решили, при наведении курсора на редактируемый текст его фон должен меняться на светло-голубой. Чтобы осуществить это, мы используем класс editHover.
$(".editable").hover(
function()
{
$(this).addClass("editHover");
}, 
function()
{
$(this).removeClass("editHover");
}
);

Этот небольшой кусочек кода сделает все, что нужно. Для редактирования класса editHover мы будем использовать функцию hover, доступную в jQuery – таким образом, элемент веб-страницы будет менять цвет, когда на него наводят курсор, и принимать прежний цвет, когда курсор убирают. Эта функция используется для выделения специфических элементов – бессмысленно применять его для всех элементов веб-страницы.

Переключение элементов

Прежде всего, нам необходимо удостовериться, что наш код сработает после двойного щелчка мыши по редактируемому элементу, поэтому давайте предусмотрим это в коде.
$(".editable").bind("dblclick", replaceHTML);

Мы прикрепили функцию replaceHTML (замена HTML) к событию double click (двойной щелчок мыши) в случае, если событие относится к редактируемому элементу. Теперь продолжим работу над организацией переключения элементов.
function replaceHTML()
{
oldText = $(this).html()
.replace(/"/g, """);
$(this).html("")
.html("<form><input type=\"text\" class=\"editBox\" 
value=\"" + oldText + "\" /> </form><a href=\"#\" class=\"btnSave\">Save changes</a> 
<a href=\"#\" class=\"btnDiscard\">Discard changes</a>");
}

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

В процессе работы над функционалом я присваиваю каждой функции имя вместо того, чтобы оставить ее анонимной, потому что собираюсь использовать эту функцию много раз. Затем мы последовательно сохраняем содержание элемента для дальнейшего использования с помощью функции html jQuery.

Теперь наш текст надежно сохранен, доступен для дальнейшего использования, и мы можем заняться переключением элементов. Прежде всего мы обнуляем элементы li пустой строкой функции html, затем добавляем несколько стандартных HTML-элементов для создания окна ввода текста, а также парочку ссылок для сохранения и отмены редактирования. Также мы распределили все элементы по классам, чтобы обеспечить простой доступ кода к ним.

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

Сохранение редактирования
$(".btnSave").live("click", 
function()
{
newText = $(this).siblings("form")
.children(".editBox")
.val().replace(/"/g, """);

$(this).parent()
.html(newText);
}
); 

Прежде всего, позвольте представить вам функцию jQuery, которая называется live. Не исключено, что раньше вам не приходилось с ней сталкиваться, так что я в двух словах расскажу, что это такое.

Вы не можете привязывать совершение определенных действий к элементам, которые еще не представлены в объектной модели документа при загрузке страницы и структуры JavaScript – поэтому, если вы попробуете использовать стандартные связывающие функции, у вас ничего не получится. Однако функция live решит эту проблему.

Эта функция связывает операции и события вне зависимости от того, когда был создан документ. Более подробная информация о функции доступна в официальных документах фреймворка.

Теперь давайте посмотрим на наш код внимательно. Сначала мы связываем код, содержащийся в анонимной функции, с событием «щелчок мыши». С помощью функции мы сохраняем текст, содержащийся в поле ввода – это довольно ловкий ход, ведь этот текст еще не имеет собственной идентификации на странице. Затем мы переходим от первоначального элемента к элементу, созданному пользователем, а после этого сохраняем его.

Далее мы обращаемся к исходному элементу и заменяет его HTML-содержимое текстом, сохраненным на предыдущем шаге.

Эта часть кода вполне могла уместиться в одну строчку, но я разбил ее на несколько строчек для более удобной читабельности кода.

Отмена редактирования
$(".btnDiscard").live("click", 
function()
{
$(this).parent()
.html(oldText);
}
); 

Это на самом деле очень просто – никаких подводных камней, все в точности так, как написано. Если пользователь передумал сохранять отредактированный текст, то мы просто заменяем HTML-содержимое исходного элемента текстом, который содержался в элементе изначально и был сохранен с помощью функции oldText.

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

Связывание и разъединение

Если вы уже попробовали протестировать написанный код, то наверняка столкнулись с такой ошибкой: когда пользователь дважды щелкает мышкой по окну ввода текста, в нем появляется HTML-код системы редактирования. Попробуйте – с каждым двойным щелчком к нему будет добавляться все новые и новые строки текста. Еще хуже будет, если вы выберете для сигнала к началу редактирования одинарный щелчок мыши.

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

Следует слегка изменить написанный нами код:
function replaceHTML()
{
//Code
$(this).html("")
// Earlier form insertion code
.unbind('dblclick', replaceHTML);
}

Мы сделали так, что сигнал «начало редактирования» не работает в случае, если редактирование уже началось. Для остальных редактируемых элементов страницы сигнал по-прежнему действует.
$(".btnSave").live("click", 
function()
{
// Earlier code
$(this).parent()
.html(newText)
.bind("dblclick", replaceHTML); 
}
);

$(".btnDiscard").live("click", 
function()
{
$(this).parent()
.html(oldText)
.bind("dblclick", replaceHTML); 
}
); 

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

Последние штрихи

Последняя часть нашего кода направлена исключительно на внешнюю эффектность действий программы. Возможно, вы обратили внимание на то, что элементы класса li отличаются красивым выравниванием, но при редактировании текста оно исчезает, что портит всю картину. Мы же хотим, чтобы отредактированный текст был выровнен точно так же, как исходный, поэтому добавляем элементу класс noPad при начале редактирования.
function replaceHTML()
{
//Code
$(this).addClass("noPad")
.html("")
// Earlier code
}

Напоминаю, что при исправлении одного элемента можно параллельно редактировать все остальные элементы веб-страницы.
$(".btnSave").live("click", 
function()
{
// Earlier code
$(this).parent()
.removeClass("noPad") 
// Earlier code 
}
); 

$(".btnDiscard").live("click", 
function()
{
$(this).parent()
.removeClass("noPad")
// Earlier code
}
); 

И еще один шаг:

Чтобы не делать код слишком длинным, я ограничился одним вариантом функционала. Если же вы решите использовать этот функционал в собственных проектах, то вам потребуется предусмотреть использование систем не-SAP, а, кроме того, модель AJAX.

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

Для добавления AJAX-запроса следует отредактировать функцию сохранения следующим образом:
$(".btnSave").live("click", 
function()
{
newText = $(this).siblings("form")
.children(".editBox")
.val().replace(/"/g, """);

$.ajax({
type: "POST",
url: "handler.php",
data: newText,
success: function(msg){
// Some code here to reflect a successful edit;

}
});

$(this).parent()
.html(newText)
.removeClass("noPad")
.bind("dblclick", replaceHTML);
}
); 

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

Выводы

Ну вот и все – у вас есть код, который вы можете добавить в свои веб-разработки. Надеюсь, что урок вам понравился, и вы смогли почерпнуть из него нечто интересное и полезное. Смело используйте код в своих целях и обращайтесь ко мне, если что-то будет непонятно.
Добавлено: 21 Августа 2013 01:19:18 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...