Пишем регистрацию на PHP и JavaScript – Часть 2: JavaScript

Всем привет! Сегодня у нас вторая часть статьи о регистрации, посвященная валидации на клиенте с помощью JavaScript.

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

Перейдём к практике

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

1
2
3
function ge(id) {
	return document.getElementById(id);
}


В JavaScript есть обработчик события onsubmit, срабатывающий при попытке отправки формы. Как и любой другой обработчик, если он возвращает false, то браузер не выполняет стандартные действия, связанные с этим событием, в данном случае это отправка формы. В этом обработчике будет производиться вызов функции, которая будет выполнять проверку данных, которые содержит форма. В случае, если данные корректны она вернёт true и форма оправится. Если данные некорректны она вернёт false и форма не отправится. Добавим этот обработчик события к форме:

1
2
3
<form id="reg_form" action="index.php" method="post">
<!—содержимое формы -->
</form>


В самом начале определим текст, который будет выводиться при разных видах ошибок:

1
2
3
4
5
6
var emptyField = 'Заполните поле!',
	shortLogin = 'Cлишком короткий логин!',
	shortPass = 'Слишком короткий пароль',
	notEqualPass = 'Пароли не совпадают!',
	badMail = 'Плохое мыло!',
	notUniqueLogin = 'Пользователь с таким именем уже зарегестрирован!';


Создадим объект XMLHttpRequest для оправки ajax-запроса. Это нужно для проверки уникальности логина перед отправкой.

var  req = false;
if(window.XMLHttpRequest)
	req = new XMLHttpRequest();
else if(window.ActiveXObject)
	req =  new  ActiveXObject("Microsoft.XMLHTTP");


На нашем сайте при регистрации пользователь должен указать логин, пароль и e-mail. Все поля должны быть заполнены т.е. содержать что-то, кроме пробелов. Напишем функцию, которая будет определять “пуста” ли строка. В дальнейшем под “пустой” строкой будем понимать либо ‘’, либо строку содержащею только пробелы.

function isEmptyStr(str) {
	if(str == "") return true;
	var count = 0;
	for(var i = 0; i &lt; str.length; ++i)
		if(str.charAt(i) == " ") ++count;
	return count == str.length;
}


Думаю, тут всё понятно. Если строка не равна “”, то считаем количество пробелов в ней и, если количество пробелов равно длине строки, то считаем её пустой.

Приступим к написанию тела функции isValidForm(). Все последующие функции будут вложены в неё. Если какое-то поле заполнено некорректно, то будут выполняться схожие операции над ним. Напишем для выполнения этих операций функцию:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function notValidField(field, str) {
		field.value = str; // Выводим инфу об ошибке в поле
		field.error = true; // Запоминаем, что поле заполнено не верно
		valid = false; // Считаем форму не валидной
		/* Вешаем обработчик события, который будет очищать поле от
			информации об ошибке при фокусе.
			При потере фокуса поля с type="password" меняют type на
			"text", чтобы информация об ошибках не заменялась звёздочками.
			При фокусе на эти поля им необходимо вернуть назад их родной type
		*/
		field.onfocus = function () { 
			if(field.id == 'pass' || field.id == 're_pass') field.type = 'password';
			if(field.error) field.value = '';						
		}
		// Обработчик, который проверяет поле на корректность при потере им фокуса.		
		field.onblur = function () {			
			if(isEmptyStr(field.value)) {
				notValidField(field, emptyField);
				if(field.id == 'pass' || field.id == 're_pass') field.type = 'text';
			} else
				field.error = false;
			switch(field.id) {
				/*Функции checkLogin(), checkMail() и  checkNoXyz()
					выполняют проверку полей по дополнительным параметрам,
					разным для каждого поля.
				*/
				case 'login' : checkLogin(); break;					
				case 'mail' : checkMail();
				case 'no_xyz': checkNoXyz();
			}			
		}		
	}


В качестве параметров она принимает некорректно заполненное поле и текст, который будет в нём отображатся. Эта функция будет вложена в isValidForm().

Напишем функции для проверки полей по дополнительным параметрам. Для проверки поля логин:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function checkLogin() {
		/*Логин не может быть короче 5 символов.
			Выводим инфу о том, что логин слишком короткий только если поле
			было заполнено ранее (!login.error).			
		*/
		if(login.value.length &lt; 5 &amp;&amp; !login.error) {
			notValidField(login, shortLogin);
		} else if(!login.error) {
		/* Если логин достаточно длинный, то отправляем асинхронный запрос
			для проверки его уникальности.
		*/
			req.open('GET', 'index.php?isset_login=' + encodeURIComponent(login.value), false);			
			console.log('index.php?isset_login=' + encodeURIComponent(login.value));
			if(req.readyState == 4  &amp;&amp; req.status  ==  200) {
				/*Если пользователь с таким логином уже есть, то
					выводим инфу об этом в поле.
				*/
				if(req.responseText == '1')
					notValidField(login, notUniqueLogin);
			}
		}			
	}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function checkPass() {
		if(!pass.error &amp;&amp; !rePass.error) {
			//Проверяем пароли на длинну и совпадают ли они.
			if(pass.value.length &lt; 5 &amp;&amp; pass.value == rePass.value) {
				notValidField(pass, shortPass);
				notValidField(rePass, shortPass);
				/*Меняем type на text, чтобы не отображаль звёздочки,
					как при вводе пароля.
				*/
				pass.type = 'text';
				rePass.type = 'text';
			//Аналогично, если пароли не совпадают.
			} else if(pass.value != rePass.value) {
				notValidField(pass, notEqualPass);
				notValidField(rePass, notEqualPass);
				pass.type = 'text';
				rePass.type = 'text';
			}
		}
	}


И функции для проверки e-mail и чекбокса. Тут всё просто: проверяем мыло на соответствие регекспу (регулярному выражению), чекбокс – на наличие галочки (checked=”true”). Регексп длинный и ужасный. Не будем вдаваться в подробности его работы.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function checkMail() {
		if(!mail.error &amp;&amp; !/^([a-z0-9])(\w|[.]|-|_)+([a-z0-9])@([a-z0-9])([a-z0-9.-]*)([a-z0-9])([.]{1})([a-z]{2,4})$/i.test(mail.value))
			notValidField(mail, 'Плохое мыло!');
	}
 
	function checkNoXyz() {
		var el = ge('no_xyz');
		if(!el.checked) {
			ge('text_no_xyz').innerHTML = 'Галочку поставь, блять!';
			valid = false;
			el.onchange = function () {
				if(el.checked) ge('text_no_xyz').innerHTML = 'Обязуюсь не творить хуйни!'
			}
		}
	}

Теперь отбираем все нужные нам элементы по id, проверяем “заполненность” полей с type=”password” и type=”text” (не забываем, что в форме есть чекбокс) и вызываем все предыдущие функции для проверки логина, паролей, мыла и чекбокса. Всё это делаем в функции isValidForm().

var elements = ge('reg_form').elements,
		login = ge('login'),
		pass = ge('pass'),
		rePass = ge('re_pass'),
		mail = ge('mail'),
		valid = true;
	//Проверяем поля с type="password" и type="text" на "заполненность"
	for(var i = 0; i &lt; elements.length; ++i) {
		if(elements[i].error) valid = false;
		if((elements[i].type == 'text' || elements[i].type == 'password') &amp;&amp; isEmptyStr(elements[i].value)) {
			notValidField(elements[i], emptyField);
			elements[i].type = 'text';
		}
	}
	/*Выполняем дополнительную проверку полей по параметрам,
		разным для каждого поля
	*/
	checkLogin();	
	checkPass();
	checkMail();
	checkNoXyz();	
	return valid;


Это всё! Регистрация работает красиво и быстро. На этом с ней покончено 🙂
Чтобы не пропустить следующие статьи в рубрике «Сайт с нуля», подпишитесь на RSS.
У нас на очереди статья о авторизации на сайте. До встречи!

Пишем регистрацию на PHP и JavaScript – Часть 1: PHP

  • А парсеры вы не пишете?
    а то есть одна идейка-)

  • Нет, пока что парсеры мы не писали. А что за идейка? Подумать можно, вдруг осуществимо)
    Дело в том, что регулярные выражения, необходимые для парсеров я уж очень не люблю)

  • Ну идея стандартная, парсинг контента + пропуск через синонимайзер, правда чтобы все шло в авторежиме

    Если будете готовы как-нибудь написать, стучите в ICQ-)

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

  • Занятно, что все на чистом JS — давненько такого не видел. Вообще респект и уважение, но… не проще ли подключить jQuery, он нынче уже де-факто в сайтостроительстве. А код становится читабельнее.

  • seran4ek, регекспы используются. Для проверки мыла. В других случаях не вижу целесообразность в их использовании. Зачем они нужны? Для определения длинны строки? Хотя при помощи регекспов можно было бы считать пробелы в строке.

    ZeroXor, безусловно, подключение jQuery всё упрстит, но это довольно тяжёлый фреймворк. Догружать ещё 55-120кб для такой простой задачи нецелесообразно, это увеличит скорость загрузки странички, хоть и не намного. Если и подключать jQuery то надо исполбзовать функционал этого фрймворка наполную.

  • Это всё прекрасно, но вы слишком быстро перешли от раскладывание по полочкам к грубому метанию снарядов.
    Я к сожалению не понял, в каком порядке это писать и в какой файл.
    Не могли бы вы разъяснить, пожалуйста 🙂

  • Дима, весь JavaScript-код сохранейте во внешний файл. Назовите его regestration.js, например. Подключается этот файл в head’е в файле index.php вот так:

    1
    
    <script type="text/javascript" src="regestration.js"></script>

    Форма сохраняется в файл index.php
    Весь js-код можно посмотреть тут:
    http://true-coder.ru/files/regestration.js
    Только учтите, что всё это вырванное из контекста работать не будет. Чтобы научится делать нечто подобное читайте первую часть статьи.

    • получается ли, что весь код с этой статьи можно разместить в одном файле и вызвать этот файл в «/registration/template/registration.php»?

  • Иван

    По больше статей пишите) в ,,сайт с нуля,,)спасибо)очень полезно)

  • Сергей

    true-coder, ты наверное из троицы самый резвый. Извини конечно, но у тебя все просто. Ты прикинься новичком и попробуй совместить первый пост со своим вторым. Можно даже с учетом своего совета! Я понимаю что додумывать полезно. Но ведь это «сайт с НУЛЯ!» Разве нет?

    Подключается этот файл в head’е в файле index.php вот так:

    чет не видел я тега head в первых двух постах… ребят посогласованей!
    Извините за критику.
    А так спасибо полезный материал, самое главное грамотно продуманый и на чистом коде. Молодцы спасибо еще раз!

  • Сергей, тег head должен быть в любом валидном html-документе. То, что его нет в листингах кода в статье, не значит, что его не должно быть в работающем окончательном варианте скрипта.

    За критику спасибо. Мне всегда важно знать мнение читателей и моих статьях.

  • Сергей

    Антон Кургузенков, тогда я не знаю почему у меня появилось впечатление того что эти статьи написаны для тех кто только начинает писать свои сайты. И раз уж об этом упоминалось где-то, я считаю что должно писаться в соответствии с написанным ранее… Это я знаю что в HTML должны быть определенные теги, а другие парни только пытающиеся что то самостоятельно сделать не сразу сообразят. да и вряд ли без посторонней помощи разберутся с данной статьей, перепишите, дополните или что нибудь сделайте. Вот первая статья очень понравилась. все четко, структура папок, объяснения что для чего. Хотелось бы все статьи видеть в подобном стиле.

    И подскажите, как найти следующие статьи? они уже написаны?!

  • Mr.MYSTIC

    function isEmptyStr(str) {
    if(str == "") return true;
    var count = 0;
    for(var i = 0; i < str.length; ++i)
    if(str.charAt(i) == " ") ++count;
    return count == str.length;
    }

    лучше заменить на
    function isEmptyStr(str) {
    if(str.trim() == "") return true;
    else return false;
    }

  • Mr.MYSTIC, Вы правы. Функция isEmptyStr не совсем удачно реализована. Но я поправлю её на несколько другой вариант.

    function isEmptyStr(str) {
    return /^\s*$/.test(str);
    }

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

    В Вашем варианте есть один минус: он не будет работать в старых браузерах, где нет функции trim.

  • Андрей

    Спасибо за статью , может для наглядности большей как в прошлой статье архив с иходниками выложишь?

  • В’ячеслав

    Андрей буду благодарен если дадите архив исходников, хочу посмотреть как это все выглядит как бы сказать в теле. savalv@list.ru Заранее спасибо!

  • Andrey

    Можете мне все файлы прислать, а то я что то не пойму.

    kop_and@mail.ru

  • Максим

    Здравствуйте уважаемые web-разработчики. Файл registration.js должен лежать в той же директории, где находится файл index.php, не так ли? Тогда присоединение registration.js будет выглядеть так:

    Demo


    Спасибо

  • Максим

    Извините не могу оставить пример кода. Просто Demo остается. Не могу понять почему.

  • Ildar

    После заполнения полей регистрации у меня выводит:
    «Вы ошиблись при наборе URL в браузере. Вероятнее всего, сервер пытается найти файл Z:/home/akhmetzyan.local/www/registration/template/index.php, которого не существует.»

    Что делать?

  • Ildar

    а если index.php поместить в template то:
    Parse error: syntax error, unexpected T_STRING in Z:\home\akhmetzyan.local\www\registration\template\index.php on line 18

  • Bibiqon

    Дима, весь JavaScript-код сохранейте во внешний файл. Назовите его regestration.js, например. Подключается этот файл в head’е в файле index.php вот так:

    1

    Форма сохраняется в файл index.php
    Весь js-код можно посмотреть тут:
    http://true-coder.ru/files/regestration.js
    Только учтите, что всё это вырванное из контекста работать не будет. Чтобы научится делать нечто подобное читайте первую часть статьи.

    Полагаю что к этому необходимо еще добавить что в форме должен быть сам метод onsubmit:

    Или не?

  • Bibiqon

    Мде…. называется показал код =) form id=»reg_form» action=»index.php» method=»post» onSubmit=’return isValidForm();’

  • Неплохо бы исходники выложить

  • Геннадий

    Здравствуйте! Я новичок. Не пойму, как после регистрации на странице появляется мой логин, имя, и т.д.? Как мое имя вписывается!?

  • Сергей

    В JavaScript есть обработчик события onsubmit

    а какую функцию вызывть?
    isValidForm()???

  • Berkos

    Спасибо за позновательную статью. Хотя если честно, первую часть понял всю, а вот с JavaScript у меня не сложилось и как проверку прикрутить к формочке регистрации не доходит. Если возможно, можете дописать как это выглядит со стороны регистрационной формы

  • Дмитрий

    А обработчик ajax запроса где? А при регистрации отправка на e-mail письма с активацией? Я бы сказал что тут только половина регистрации

  • Vlada

    я не поняла куда идет рассматриваемый код и где всё это должно находиться

  • Константин Герасимов

    Коллеги, проверьте пожалуйста часть кода, отвечающего за уникальность логина.
    Не выходит предупреждение при регистрации одинаковых логинов.

  • Сергей Гейн

    А что делает i &lt?
    Извиняюсь если глупость, но подскажите пожалуйста!!