Передача смешанной формы через ajax + iframe

версия для печати

Об этом давно и много написано. Причиной появления этой статьи стал тот печальный факт, что когда мне понадобилось решение, были потеряны часы на поиск и разбор неработающих скриптов и корявых библиотек. Видимо, мне так "свезло", раз не смог решить проблему быстро. Суть остается прежней: нужно передать с формы файл и данные без перегрузки страницы, подобно ajax-запросу. Используем jQuery и iframe.

Собственно почему jQuery/ajax, а не чистый javascript? Потому что так пример получится проще и скорее всего его практическое применение будет на сайтах, уже использующих библиотеку jQuery. Почему iframe? Это "дедовский" способ, работаюший даже в старых браузерах. Если нужно решение только под HTML5, тогда читайте про объект formData и XMLHttpRequest Level 2, с ними жизнь стала проще :)

Весь пример в архиве. Частичный код с пояснениями ниже.

<form id='formMessage' method='POST' target='uptarget' enctype='multipart/form-data' action='script.php'>
    <input type='text' name='textField' value='some text'/><br>
    <input type='file' name='attachment'><br>
    <input type='submit' value='Отправить'>
</form>

<iframe name="uptarget" id="upframe" style="width:0px; height:0px; position:absolute; top:-9999px;"></iframe>

<div id='postRslt'></div>

Обычная форма со всеми нужными атрибутами. Важным является атрибут target, содержащий имя фрейма, куда будет отправлен ответ сервера после передачи с формы. Если не указывать этот приемник, тогда ответ сервера перепишет текущую страницу. Т.е. без правильного target получим поведение браузера, аналогичное обыкновенной отправке с формы. Только при этом от сервера не будет указания редиректа, т.к. он по замыслу должен вернуть только инфу об успешной/неудачной обработке данных. Фигня короче получится..

Тег iframe позволяет загрузить в себя отдельный документ, хотя при этом сам располагается с теле текущего документа. Используем его, чтобы принять нужный ответ сервера. А чтобы не палить его юзеру, задаем минимальные размеры фрейма и смещаем его за пределы окна. Прячем именно смещением, т.к. использование css-свойства " visibility:hidden " не помогает в FF и Opera, все равно остается маленький квадратик.

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

$(document).ready(function() {
    //Вешаем обработчик на событие "Форма отправлена"
    $('#formMessage').submit(function() {
        //Обработчик события "Документ загружен"
        $('#upframe').load(function() {
            //Вытаскиваем из фрейма ответ сервера
            var rslt = $(this).contents().find('body').html();
            //Выдаем этот ответ, как результат загрузки с формы
            $('#postRslt').html(rslt);
        });
    });
});

Добавить к комментариям нечего. Лучше расскажу про другой вариант. Фрейм можно создавать и удалять динамически. Не знаю, на сколько это удобно и нужно, просто можно. Из html убираем iframe, а в скрипте пишем:

$(document).ready(function() {
     //глобальная переменная, чтоб несколько отправок не плодили фреймы
     var iframe;

    //Вешаем обработчик на событие "Форма отправлена"
    $('#formMessage').submit(function() {
        //создаем фрейм только на время отправки данных с формы
        if (iframe
== undefined) {
            iframe = $('<iframe name="uptarget" id="upframe" style="width:0px; height:0px; position:absolute; top:-9999px;"></iframe>');
        }
        $('#formMessage').append(iframe);
        //это необязательно, если параметр формы уже задан
        $('#formMessage').attr('target', 'uptarget');

        //Обработчик события "Документ загружен"
        $('#upframe').load(function() {
            //Вытаскиваем из фрейма ответ сервера
            var rslt = $(this).contents().find('body').html();
            //Выдаем этот ответ, как результат загрузки с формы
            $('#postRslt').html(rslt);
            //Удаляем фрейм.. и это приводит в багу в Opera
            iframe.remove();
        });
    });
});

При такой реализации получается баг в Opera 12. Удаление фрейма как-то обнуляет значение переменной rslt и юзер не увидит ответ. Не знаю, почему так. В FF и Chome все отлично работает.

Пару слов о серверном скрипте, хотя там почти ничего интересного.

header('Content-type: text/html; charset=utf-8');

//Валидация и сохранение файла
//...
echo 'Получен файл:';
var_dump($_FILES);

//Валидация данных и запись их куда-нибудь
//...
echo 'Получены данные:';
var_dump($_POST);

Текст скрипта сохраняем в кодировке UTF-8 без BOM для того, чтобы крякозябры не получить в ответе. Обязательно без BOM, иначе наступите на те же грабли, что и я. Или вообще сохраните его в ANSI и поменяйте в отправляемом заголовке кодировку на windows-1251 :-)

Справедливости ради нужно заметить, что использования ajax здесь вообще нет, назначенный обработчик load(), и тот работает после наступления события. Реальная загрузка проводится только по html-описанию. Просто поведение страницы похоже на ajax-запрос, отсюда и название. Эдакий маркетинговый ход статьи :)

[1oo%, EoF]

Понравилась статья? Расскажите о ней друзьям:


Комментарии
Для работы модуля комментариев включите javaScript


Показать/скрыть правила
Имя
[i] [b] [u] [s] [url]
:-) ;-) :D *lol* 8-) :-* :-| :-( *cry* :o :-? *unsure* *oops* :-x *shocked* *zzz* :P *evil*

Осталось 1000 символов.
Код защиты от спама Обновить код
Каждый комментарий проходит ручную модерацию. 100% фильтрация спама.
Продвижение
Время
Метки