jQuery: определить, нажата ли определенная кнопка в обработчике события «изменить»

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

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

У меня есть текстовое поле, оформленное так, чтобы оно выглядело нередактируемым, с кнопкой «редактировать» рядом с ним, которая при нажатии превращается в кнопку «сохранить» и делает текстовое поле редактируемым.

$(".edit_btn").click(function() {
   // make the associated text field look editable, and change this 'edit'
   // button into a 'save' button. Then place focus on the text in
   // in the field.
});

$(".save_btn").click(function() {
   // if (value in associated text field has changed from when page loaded)
   //   submit the form and save this new text
   // else 
   //   revert to non-editable mode (hide this 'save' button, 
   //   and show 'edit' button)
});

Все это работает нормально. Однако, если пользователь покидает редактируемое текстовое поле, я хочу иметь возможность определить, покинули ли они поле, чтобы нажать кнопку «Сохранить», или просто щелкнули где-нибудь еще на странице. Поэтому в обработчике событий «размытие» для поля мне нужно знать, нажал ли я кнопку «Сохранить». Для жизни я не могу понять, как это сделать:

$('input[name^="tgfld_"]').blur(function() {
    // if (save button has been clicked) <- this is the problem
    //   don't do anything since the save function will handle this
    // else if (value in the field hasn't changed)
    //   revert everything back to non-editable mode
    // else if (value in the field HAS changed)
    //   do a window.confirm and prompt the user to click the 'save' 
    //   button to save their changes
});

Таким образом, он определяет, была ли кнопка сохранения причиной срабатывания «размытия» - вот что я не могу понять.

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

(Я должен отметить, что на странице может быть много таких комбинаций полей/кнопок, но пока это ни на что не влияет.)


person TMA-1    schedule 11.10.2011    source источник
comment
Это вообще помогает? stackoverflow.com/q/6411271/497356   -  person Andrew Whitaker    schedule 11.10.2011


Ответы (2)


Введение

Это очень интересная (и нетривиальная ИМХО) проблема. Чтобы решить эту проблему, я сначала создал образец страницы с несколькими «группами» текстовых полей ввода и кнопок, которые их включают:

введите здесь описание изображения

Согласно вашему вопросу, поле ввода по умолчанию отключено и становится «редактируемым», нажав кнопку.

Лучшим решением оказался простой конечный автомат. Конечный автомат помогает понять (большое количество) событий, запускаемых браузером, просматривая только те события, которые относятся к текущему состоянию, и игнорируя все остальные.

Реализация

Ниже представлена ​​схема конечного автомата (текст рядом с каждой стрелкой перехода указывает источник и имя события, запускающего этот переход):

введите здесь описание изображения

Решение в действии (проект JS Fiddle)

Для справки я также включаю сюда код JavaScript:

function makeEditable(inputId, btnId) {
    var state = "Locked", timeout = null, $input = $("#" + inputId), $btn = $("#" + btnId);

    function setStateNow(precondition, newState, e) {
        if (!precondition || state === precondition) {
            if (window.console) { window.console.log("State change: " + state + " => " + newState + " (from " + e.target.id + "." + e.type + ")"); }
            if (newState === "Locked") { // changing from any state to Locked
                $input.prop("disabled", true);
                $btn.val("Edit");
            } else if (state === "Locked") { // changing from Locked to any other state
                $input.prop("disabled", false).focus();
                $btn.val("Save");
            }
            if (newState === "LockPending") { // changing from any state to LockPending
                timeout = setTimeout(
                    function () { setStateNow("LockPending", "Locked", { target: { id: e.target.id }, type: "setTimeout" }); },
                    20);
            } else if (state === "LockPending") { // changing from LockPending to any other state
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = null;
                }
            }
            if (newState === "Editable" && state === "LockPendingMouse") {
                $input.focus();
            }

            state = newState;
            return true;
        }
        return false;
    }


    function setState(e) {
        var r;
        if (e.data.rules) {
            for (i in e.data.rules) {
                r = e.data.rules[i];
                if (setStateNow(r.precondition, r.newState, e)) {
                    return;
                }
            }
        } else {
            setStateNow(e.data.precondition, e.data.newState, e);
        }
    }


    $input
        .focus ({ precondition: "LockPending", newState: "Editable" }, setState)
        .blur({ precondition: "Editable", newState: "LockPending" }, setState);

    $btn
        .click({ rules: [{ precondition: "Locked", newState: "Editable" }, { precondition: "LockPendingMouse", newState: "Locked" }, { precondition: "Editable", newState: "Locked" }] }, setState)
        .mousedown({ precondition: "Editable", newState: "LockPendingMouse" }, setState)
        .mouseleave({ precondition: "LockPendingMouse", newState: "Editable" }, setState)
        .focus ({ precondition: "LockPending", newState: "Editable" }, setState)
        .blur({ precondition: "Editable", newState: "LockPending" }, setState);
}

Код определяет одну функцию, makeEditable. Эта функция принимает идентификатор элемента управления вводом и идентификатор соответствующей кнопки, что делает ее редактируемой. Затем он создает замыкание с «частным» конечным автоматом. Это означает, что у нас будет один конечный автомат на каждый вызов makeEditable (поскольку разные группы кнопок ввода могут находиться в разных состояниях).

Фактическое «мясо» обработки изменения состояния (создание текстового поля редактируемым или отключенным) можно найти в частной функции setStateNow, когда текущее или новое состояние равно Locked.

Заключение

Я успешно протестировал решение в Chrome 14, Opera 10.51, IE 9, FireFox 5.0.1, Safari 5.1, Mobile Safari (iPad2).

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

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

person Milan Gardian    schedule 11.10.2011

Я уверен, что есть, вероятно, гораздо лучшее решение, но вот что я придумал:

$(document).ready(function() {
    saved = false;

    $('#button').click(function() {
        saved = true;
    });

    $('#input').blur(function() {
        var t = setTimeout("if(!saved) {alert('not saved!');}",400);
    }); 
});

пример здесь

Я ввел небольшую задержку с setTimeout, потому что кажется, что при нажатии на кнопку событие blur всегда срабатывает первым перед событием кнопки click.

person Brian Glaz    schedule 11.10.2011
comment
Я думал об этом как о запасном решении, если что-то более элегантное или умопомрачительно очевидное не ударит меня по лицу. Однако спасибо за напоминание, так как я начал думать, что мой резервный вариант заключается в том, чтобы обнаружить координаты X, Y мыши в обработчике размытия полей и посмотреть, попадают ли они на соответствующую кнопку. Это просто сложно. - person TMA-1; 11.10.2011
comment
На самом деле вы можете использовать этот метод и применять его ко всем элементам с определенным классом, а не использовать идентификаторы. Каждый вход может хранить свою собственную «сохраненную» переменную, используя метод jQuery .data(). - person Brian Glaz; 11.10.2011