Jun 15

Доста често напоследък ми се налага да правя динамични страници изцяло задвижвани от javascript, като при тях постоянно се добавят и махат dom елементи. Което значи непрекъснато да се добавят и махат event хандлъри, което е меко казано досадно и води до много грешки. Но с вградените в CD3.Behaviors event delegation функции (за тях специално ще има поне един цял пост) тази работа е много лесна, просто делегирам всички действия към елементи които няма да се променят.

До тук всичко звучи много добре, но както винаги IE се появява на сцената с бъг, който е, че submit действието няма bubbling (т.е. не се делегира към родителските елементи на формата). Което е в пълен разрез със спецификация, но какво да се прави свикнали сме.

Този проблем го знам от много време и винаги го заобикалях по един или друг начин. Но наскоро на колега му трябваше бързо решение, което да може да ползва на 2 – 3 места затова за няколко минути написах това за Prototype.js:

Element.addMethods({
    delegateSubmit: function(element, callback){
        return $(element)
            .observe('click', function(e){
                if (e.findElement('form') && e.findElement('input[type=submit],input[type=image]'))
                    callback.call(this, e);
            })
            .observe('keyup', function(e){
                if (e.keyCode  == Event.KEY_RETURN && e.findElement('input') && e.findElement('form'))
                    callback.call(this, e);
            })
        }
});

Като цяло това, което прави тази функция е, че наблюдава за натискане на submit или image бутони или за натискане на enter върху някои input. Лошото в случая е, че страдат и нормалните браузъри като Firefox или Safari.

Затова направих нова версия, която засича дали submit се делегира (за начина, по който разбирам пише по-подробно в тази статия – Detecting event support without browser sniffing)

Element.addMethods({
    delegateSubmit: (function(){
        var el = document.createElement('div'), isSupported = 'onsubmit' in el;

        if (!isSupported){
            el.setAttribute('onsubmit', 'return;');
            isSupported = typeof el.onsubmit == 'function';
        }

        return isSupported ? function(element, callback){
            return Event.observe(element, 'submit', callback);
        } : function(element, callback){
            return $(element)
                .observe('click', function(e){
                    if (e.findElement('form') && e.findElement('input[type=submit],input[type=image]'))
                        callback.call(this, e);
                })
                .observe('keyup', function(e){
                    if (e.keyCode  == Event.KEY_RETURN && e.findElement('input') && e.findElement('form'))
                        callback.call(this, e);
                    })
        };
    })()
});

Това е доста по-добро решение което оправя проблема със submit само, когато има такъв проблем.

Тук бих могъл примерно да използвам Function.wrap върху Event.observe, но нещо не съм фен такива monkey patching неща. А и по-скоро това и хака за делегиране на focus/blur под IE ще са част от моята Event.delegate, която ако имам късмет ще е част от Prototype.js.

Ако някой има по-елегантно решение, няма да му се разсърдя ако го сподели.

4 коментара за "Просто делегиране на submit събития"

  1. Никола каза:

    Не съм сигурен, дали jQuery не прави магии (силно ме съмнява), но submit bubbling си работи идеално в ИЕ6 на един проект, който скоро ще е онлайн. Но ето кодът:

    // handle form submissions
    else if (e.target.type == 'submit') {
    ajaxCall();
    return false;
    }

  2. Radoslav Stankov каза:

    То би трябвало да прави нещо защото по принцип има проблем със sumit и bubbling от самия IE. Но тъй като не работя с jquery не съм много запознат с това. Предполагам че може нещо от jQuery.events.handle и jQuery.events.trigger да е.

  3. Никола каза:

    И пропуснах да кажа – има един контейнер, в който асинхронно сменям съдържанието и на този wrapper съм бинднал клик и събмит и чакам мехурчето.

    Току прегледах пак сорса на jQuery и не намирам нищо ИЕ специфично (по този проблем поне :) ). Просто за първи път чувам за такива проблеми на ИЕ, но може и да е така. Избягвам този … продукт :) .

    Инак делегирането на събмит в този проект си работи идеално – делегира се при клик на бутона, при натискане на ентър някъде по формуляра, при натискане на спейс или ентър върху бутона.

  4. Radoslav Stankov каза:

    Добре си направил, за проекта :)

    То и аз прегледах, част от кода на jQuery и не открих нищо, но тъй като jquery са се грижи за по-голямата си част от event системата(особено ако се гледал trigger-а как става) си може и да сме пропусна ли нещо.

    На мен напоследък ми се налага да имам много форми из страниците като всичките се появяват и изчезват динамично и това което правя е да делегирам submit-a и вече спрямо типа на формата(т.e. класа и) да се разправям с нея.

Какво мислите по въпроса