Почему не срабатывает preventDefault?

411
20 апреля 2017, 17:07

Есть html форма в которой пользователь может менять свои данные:

 <form action='/user/editProfile' id="edit-profile_form" method="post"> 
<ul class='profile-data'> 
    <li class='profile-data_item persona-icon'>Никнейм: <input id='profile_nickname' name='nickname' type="text" placeholder="Никнейм:" value="{{myNickname}}"   :readonly="inputStatus == 1 ? true : false"></li> 
    <li class='profile-data_item'>Имя: <input name='name' id='profile_name' type="text" placeholder="Имя:" value="{{myName}}"  :readonly="inputStatus == 1 ? true : false"></li> 
    <li class='profile-data_item'>Фамилия: <input name='surname' id='profile_surname' type="text" placeholder="Фамилия:" value="{{mySurname}}"  :readonly="inputStatus == 1 ? true : false"></li> 
    <li class='profile-data_item b-day-icon'>Возраст: <input name='age' id='profile_age' type="text" placeholder="Возраст:" value="{{myAge}}"  :readonly="inputStatus == 1 ? true : false"></li> 
    <li class='profile-data_item place-icon'><input type="text" name='city' id='profile_city' placeholder="Город" value="{{myCity}}"  :readonly="inputStatus == 1 ? true : false">, <input name='country' id='profile_country' type="text" placeholder="Страна" value="{{myCountry}}"  :readonly="inputStatus == 1 ? true : false"></li> 
    <li class='profile-data_item phone-icon'>Телефон: <input name='phoneNumber' id='profile_phoneNumber' type="text" placeholder="Телефон:" value="{{myPhoneNumber}}"  :readonly="inputStatus == 1 ? true : false"></li> 
    <input type="hidden" name="_csrf" value='{{csrfToken}}' /> 
    <input type="submit" value="Submit" id="edit-profile_submit"> 
</ul> 
</form>
И jquery/ajax код, который должен отправлять данные на сервер.

$("form").submit(function(e) {
        e.preventDefault();
        var csrf_token = {{csrfToken}};
        $("body").bind("ajaxSend", function(elm, xhr, s){
            if (s.type == "POST") {
                xhr.setRequestHeader('X-CSRF-Token', csrf_token);
            }
        });
        var pathname = window.location.href;
        $.ajax({
            type: "POST",
            url: '/user/editProfile',
            data: {
                name: $("#profile_name").val(),
                nickname: $("#profile_nickname").val(),
                surname: $("#profile_surname").val(),
                country: $("#profile_country").val(),
                city: $("#profile_city").val(),
                phoneNumber: $("#profile_phoneNumber").val(),
                age: $("#profile_age").val()
            },
            dataType: "html",
            contentType: "application/html, charset: UTF-*",
            success: function(data){
                console.log(data);
            },
        });
    });

Но по неизвестным причинам, при нажатии на submit, сайт переходит по ссылке, которая указана в action=''. Почему-то preventDefault не делает то, что должен. В чём проблема? Версия jquery 3.1.0, на сервере форму обрабатывает код...

router.post('/editProfile', isLoggedIn, function (req, res, next){
    var id = req.user._id;
    console.log(req.body);
    var nickname = req.body.nickname;
    var name = req.body.name;
    var surname = req.body.surname;
    var age = req.body.age;
    var city = req.body.city;
    var country = req.body.country;
    var phoneNumber = req.body.phoneNumber;
    User.findByIdAndUpdate(id, { $set: { nickname: nickname, name: name, surname: surname, age: age, city: city, country: country, phoneNumber: phoneNumber }}, function (err, user) {
        if (err) return handleError(err);
        console.log("ADDED!!!!!!!!!!!!!");
    });
});

В итоге форма отправляется и в базу заносятся новые данные, но страница перезагружается, хотя мне это не нужно.
Все действия происходят на странице /user/:userid

Answer 1

Если так вышло, что preventDefault не прервал отправки данных на сервер через submit, то можно добавить дополнительную проверку:

e.preventDefault();
if(!e.isDefaultPrevented()){
    e.returnValue = false;
}

Также, чтобы отправки формы не произошло - достаточно внутри обработчика, точнее внутри функции, в конце, написать return false;. Если функция возвращает false, то отправки не будет, а если true - выполниться отправка формы.

preventDefault может не срабатывать по причине того, что имеются другие обработчики, которые отправляют данные с формы, к примеру. Для такого случая можно попробовать вызвать stopImmediatePropagation, а не preventDefault. То есть так:

e.stopImmediatePropagation(); // вместо e.preventDefault();

Ну а вообще, вот этот код:

$("body").bind("ajaxSend", function(elm, xhr, s){
if (s.type == "POST") {
        xhr.setRequestHeader('X-CSRF-Token', csrf_token);
    }
});

Вы в bind к "body" устанавливаете обработчик события на ajaxSend, который срабатывает каждый раз перед отправкой Ajax-запроса, причем setRequestHeader - устанавливает значение заголовка запроса HTTP. Проблема кроется скорее всего тут и preventDefault срабатывает, но срабатывает уже другой обработчик, после preventDefault. Ну и проблемная строка кода вот эта:

var csrf_token = {{csrfToken}};
Answer 2

Что приходит в браузер вместо вот этой строки?

var csrf_token = {{csrfToken}};

Вероятно, Вам нужно:

var csrf_token = "{{csrfToken}}";
Answer 3
  1. У вас в форме уже есть скрытый input _csrf, зачем отправлять повторно в заголовке?
  2. csrf лучше хранить в подобном виде:

    <meta name="csrf" content="{{csrfToken}}">
    

    и брать его как-нибудь так:

    var _token = $('meta[name="csrf-token"]').attr('content');
    
  3. Смените:

    data: {
        name: $("#profile_name").val(),
        nickname: $("#profile_nickname").val(),
        surname: $("#profile_surname").val(),
        country: $("#profile_country").val(),
        city: $("#profile_city").val(),
        phoneNumber: $("#profile_phoneNumber").val(),
        age: $("#profile_age").val()
    },
    

    На:

    var toSend = $(this).serializeArray(); // перед вызовом ajax, здесь будут все ваши данные с формы, с абсолютно такими же значениями
    $.ajax({
    // ...
        data: toSend,
    // ...
    });
    
  4. У меня данный код сработал, но, я так понмаю, что вы используете blade и скорее всего проблема в этой строчке:

     var csrf_token = {{csrfToken}};
    

    Попробуйте сменить на:

     var csrf_token = '{{csrfToken}}';
    
Answer 4

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

Событие submit - это событие браузера "отправить форму". Но аффтар отправляет данные AJAX-запросом - зачем же тогда кнопка type='submit', зачем самому назначать событие, от которого потом нужно будет избавиться? Ну напишите type='button', прикрутите к ней .onclick - и вообще не нужны будут в обработчике ни .preventDefault(), ни return false.

Мало того, и тег <form> тут совсем не нужен, вместе с бессмысленными атрибутами action и method - это, тоже, всё потом указано в AJAX-запросе, ну и зачем он, тег <form>, для красоты?

Можно было бы подумать - что для того, чтобы удобнее было обращаться к полям формы по атрибутам name, но у тега <form> и нет такого атрибута, а в запросе аффтар собирает данные из полей по их id.

Резюме: в данном коде упаковывать поля в форму незачем (можно и в div), тип кнопки назначить button, прикрутить к ней обработчик на событие onclick - и совсем не потребуется отменять "событие браузера submit", ибо его и не будет существовать в природе.

Доклад окончен.

READ ALSO
Есть вопрос: можно или нет переместить навигацию OwlCarousel 2 на блоки с другими классами, или в дрйгой блок?

Есть вопрос: можно или нет переместить навигацию OwlCarousel 2 на блоки с другими классами, или в дрйгой блок?

Можно или нет переместить навигацию OwlCarousel 2 на блоки с другими классами, или в дрйгой блок?

258
Небольшая задачка с высотой элементов

Небольшая задачка с высотой элементов

Есть 3 таблицы, у каждой по одному столбцу и по 29 строкВысота строк разная

243
Сортировка массивов

Сортировка массивов

Входящие данные : двухмерный массив

254
Как вывести дату без точек, с пробелами?

Как вывести дату без точек, с пробелами?

Нужно вывести дату в формате среда, 19 4 2017 именно с пробелами

237