ماهو - تحميل مكتبة jquery




يتم تنفيذ معالجات الأحداث jQuery دائمًا حتى يتم تقييدها-بأي طريقة حول هذا؟ (7)

هذا السؤال لديه بالفعل إجابة هنا:

يمكن أن يكون الأمر مزعجًا أن يتم تنفيذ معالجات الأحداث jQuery دائمًا حسب ترتيبها. فمثلا:

$('span').click(doStuff1);
$('span').click(doStuff2);

يؤدي النقر فوق الامتداد إلى إطلاق doStuff1() ، متبوعًا بـ doStuff2() .

في الوقت الذي أقوم فيه بربط doStuff2 () ، أرغب في خيار ربطه قبل doStuff1 () ، ولكن لا يبدو أن هناك أي طريقة سهلة للقيام بذلك.

أفترض أن معظم الناس سيقولون ، فقط اكتبوا الكود هكذا:

$('span').click(function (){
    doStuff2();
    doStuff1();
});

لكن هذا مجرد مثال بسيط - في الممارسة ليس من السهل دائمًا القيام بذلك.

هناك حالات عندما تريد ربط حدث ما ، والكائن الذي تقوم بربطه يحتوي بالفعل على أحداث. وفي هذه الحالة ، قد ترغب ببساطة في إطلاق الحدث الجديد قبل أي أحداث أخرى.

فما هي أفضل طريقة لتحقيق ذلك في jQuery؟



أفترض أنك تتحدث عن جانب الحدث الفقاعي. سيكون من المفيد أن ترى HTML الخاص بك لعناصر span المذكورة أيضًا. لا أستطيع أن أرى لماذا تريد تغيير السلوك الأساسي مثل هذا ، لا أجده مزعجًا على الإطلاق. أقترح الانتقال إلى الجزء الثاني من الشفرة:

$('span').click(function (){
  doStuff2();
  doStuff1();
});

الأهم من ذلك ، أعتقد أنك ستجده أكثر تنظيماً إذا قمت بإدارة جميع الأحداث لعنصر معين في نفس المجموعة كما هو موضح. هل يمكن ان توضح لماذا تجد هذا مزعج؟


المبدأ القياسي هو أن معالجات الأحداث المنفصلة لا تعتمد على الترتيب الذي يطلق عليه. إذا كانوا يعتمدون على أمر لا ينبغي أن يكونوا منفصلين.

وإلا ، فسوف تسجل معالج حدث واحد على أنه "أول" ، ثم يسجّل شخص آخر معالج الأحداث الخاص به على أنه "الأول" وستعود إلى نفس الفوضى كما كان من قبل.


سؤال جيد جداً ... لقد كنت مفتوناً لذلك قمت ببعض الحفر ؛ لأولئك الذين يهتمون ، هنا حيث ذهبت ، وما توصلت إليه.

وبالنظر إلى الشفرة المصدرية لـ jQuery 1.4.2 ، رأيت هذه الكتلة بين السطور 2361 و 2392:

jQuery.each(["bind", "one"], function( i, name ) {
    jQuery.fn[ name ] = function( type, data, fn ) {
        // Handle object literals
        if ( typeof type === "object" ) {
            for ( var key in type ) {
                this[ name ](key, data, type[key], fn);
            }
            return this;
        }

        if ( jQuery.isFunction( data ) ) {
            fn = data;
            data = undefined;
        }

        var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
            jQuery( this ).unbind( event, handler );
            return fn.apply( this, arguments );
        }) : fn;

        if ( type === "unload" && name !== "one" ) {
            this.one( type, data, fn );

        } else {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                jQuery.event.add( this[i], type, handler, data );
            }
        }

        return this;
    };
});

هناك الكثير من الأشياء المثيرة للاهتمام التي تحدث هنا ، لكن الجزء الذي يهمنا هو بين السطور 2384 و 2388:

else {
    for ( var i = 0, l = this.length; i < l; i++ ) {
        jQuery.event.add( this[i], type, handler, data );
    }
}

في كل مرة نطلق عليها bind() أو one() نقوم بإجراء مكالمة إلى jQuery.event.add() ... لذلك دعونا نلقي نظرة على ذلك (خطوط 1557 إلى 1672 ، إذا كنت مهتما)

add: function( elem, types, handler, data ) {
// ... snip ...
        var handleObjIn, handleObj;

        if ( handler.handler ) {
            handleObjIn = handler;
            handler = handleObjIn.handler;
        }

// ... snip ...

        // Init the element's event structure
        var elemData = jQuery.data( elem );

// ... snip ...

        var events = elemData.events = elemData.events || {},
            eventHandle = elemData.handle, eventHandle;

        if ( !eventHandle ) {
            elemData.handle = eventHandle = function() {
                // Handle the second event of a trigger and when
                // an event is called after a page has unloaded
                return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                    jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                    undefined;
            };
        }

// ... snip ...

        // Handle multiple events separated by a space
        // jQuery(...).bind("mouseover mouseout", fn);
        types = types.split(" ");

        var type, i = 0, namespaces;

        while ( (type = types[ i++ ]) ) {
            handleObj = handleObjIn ?
                jQuery.extend({}, handleObjIn) :
                { handler: handler, data: data };

            // Namespaced event handlers
                    ^
                    |
      // There is is! Even marked with a nice handy comment so you couldn't miss it 
      // (Unless of course you are not looking for it ... as I wasn't)

            if ( type.indexOf(".") > -1 ) {
                namespaces = type.split(".");
                type = namespaces.shift();
                handleObj.namespace = namespaces.slice(0).sort().join(".");

            } else {
                namespaces = [];
                handleObj.namespace = "";
            }

            handleObj.type = type;
            handleObj.guid = handler.guid;

            // Get the current list of functions bound to this event
            var handlers = events[ type ],
                special = jQuery.event.special[ type ] || {};

            // Init the event handler queue
            if ( !handlers ) {
                handlers = events[ type ] = [];

                   // ... snip ...

            }

                  // ... snip ...

            // Add the function to the element's handler list
            handlers.push( handleObj );

            // Keep track of which events have been used, for global triggering
            jQuery.event.global[ type ] = true;
        }

     // ... snip ...
    }

في هذه المرحلة ، أدركت أن فهم هذا سيستغرق أكثر من 30 دقيقة ... لذلك بحثت عن

jquery get a list of all event handlers bound to an element

ووجدت هذه الإجابة لتكرارها حول الأحداث المرتبطة:

//log them to the console (firebug, ie8)
console.dir( $('#someElementId').data('events') );

//or iterate them
jQuery.each($('#someElementId').data('events'), function(i, event){

    jQuery.each(event, function(i, handler){

        console.log( handler.toString() );

    });

});

اختبار ذلك في فايرفوكس أرى أن events في سمة data لكل عنصر لها سمة [some_event_name] ( click في حالتنا) التي يتم بها تصفيف مجموعة من كائنات handler ، كل منها يحتوي على دليل ، مساحة اسم ، اكتب ، ومعالج. "أعتقد" ، "أعتقد" ، "يجب أن نكون نظريًا قادرًا على إضافة كائنات تم بناؤها بنفس الطريقة إلى [element].data.events.[some_event_name].push([our_handler_object); ..."

ثم أذهب لإنهاء كتابة النتائج التي توصلت إليها ... وإيجاد إجابة أفضل بكثير نشرها RusselUresti ... والتي تقدم لي شيئًا جديدًا لم أكن أعرفه عن jQuery (على الرغم من أنني كنت أحدق في الوجه مباشرة .)

ما هو دليل على أن هو أفضل موقع الأسئلة والأجوبة على الإنترنت ، على الأقل في رأيي المتواضع.

لذلك أنا أقوم بنشر هذا من أجل الأجيال القادمة ... ووسمها بمجتمع wiki ، لأن RussellUresti سبق أن أجاب على السؤال بشكل جيد.


وإليك حلًا لـ jQuery 1.4.x (للأسف ، فإن الإجابة المقبولة لم تنجح في jquery 1.4.1)

$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.bind(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    var handlers = this.data('events')[name.split('.')[0]];
    // take out the handler we just inserted from the end
    var copy = {1: null};

    var last = 0, lastValue = null;
    $.each(handlers, function(name, value) {
        //console.log(name + ": " + value);
        var isNumber = !isNaN(name);
        if(isNumber) {last = name; lastValue = value;};

        var key = isNumber ? (parseInt(name) + 1) : name;
        copy[key] = value;
    });
    copy[1] = lastValue;
    this.data('events')[name.split('.')[0]] = copy;
};

يجب أن تكون نصيحة Chris Chilvers هي المسار الأول للعمل ، لكن أحيانًا نتعامل مع مكتبات الطرف الثالث التي تجعل هذا الأمر مليئًا بالتحديات ويتطلب منا القيام بأشياء شقية ... وهذا هو الأمر. IMO هي جريمة افتراض مشابه لاستخدام! مهم في CSS.

بعد قولي هذا ، وبناء على الجواب Anurag ، وهنا بعض الإضافات. هذه الطرق تسمح لأحداث متعددة (على سبيل المثال "keydown keyup paste") ، وتحديد المواقع التعسفي للمُعالج وإعادة ترتيبه بعد حدوثه.

$.fn.bindFirst = function (name, fn) {
    this.bindNth(name, fn, 0);
}

$.fn.bindNth(name, fn, index) {
    // Bind event normally.
    this.bind(name, fn);
    // Move to nth position.
    this.changeEventOrder(name, index);
};

$.fn.changeEventOrder = function (names, newIndex) {
    var that = this;
    // Allow for multiple events.
    $.each(names.split(' '), function (idx, name) {
        that.each(function () {
            var handlers = $._data(this, 'events')[name.split('.')[0]];
            // Validate requested position.
            newIndex = Math.min(newIndex, handlers.length - 1);
            handlers.splice(newIndex, 0, handlers.pop());
        });
    });
};

يمكن للمرء أن يستقر على هذا مع الطرق التي من شأنها وضع معالج معين قبل أو بعد معالج معين.


إجابة محدثة

قام jQuery بتغيير مكان تخزين الأحداث في 1.8. الآن أنت تعرف لماذا هذه فكرة سيئة للتسكع مع واجهات برمجة التطبيقات الداخلية :)

تتوفر واجهة برمجة التطبيقات الداخلية الجديدة للوصول إلى الأحداث الخاصة بعنصر DOM من خلال كائن jQuery العام ، ولا ترتبط بكل مثيل ، وتستغرق عنصر DOM كمعامل أول ، ومفتاح ("أحداث" بالنسبة لنا) مثل المعلمة الثانية.

jQuery._data(<DOM element>, "events");

إذن ، إليك الكود المعدل لـ jQuery 1.8.

// [name] is the name of the event "click", "mouseover", .. 
// same as you'd pass it to bind()
// [fn] is the handler function
$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.on(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    this.each(function() {
        var handlers = $._data(this, 'events')[name.split('.')[0]];
        // take out the handler we just inserted from the end
        var handler = handlers.pop();
        // move it at the beginning
        handlers.splice(0, 0, handler);
    });
};

وهنا playground .

الاجابه الاصليه

بعد اكتشافSean ، تعرض jQuery جميع معالجات الأحداث من خلال واجهة data العنصر. على وجه التحديد element.data('events') . باستخدام هذا ، يمكنك دائمًا كتابة مكوّن إضافي بسيط يمكنك من خلاله إدراج أي معالج أحداث في موضع محدد.

إليك مكوّن إضافي بسيط يقوم بذلك فقط لإدراج معالج في بداية القائمة. يمكنك بسهولة تمديد هذا لإدراج عنصر في أي موقف معين. انها مجرد تلاعب مجموعة. ولكن بما أنني لم أشاهد مصدر jQuery ولا أريد تفويت أي سحر jQuery من الحدوث ، فأنا عادة أقوم بإضافة المعالج باستخدام bind أولاً ، ثم أعد تشكيل الصفيف.

// [name] is the name of the event "click", "mouseover", .. 
// same as you'd pass it to bind()
// [fn] is the handler function
$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.bind(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    var handlers = this.data('events')[name.split('.')[0]];
    // take out the handler we just inserted from the end
    var handler = handlers.pop();
    // move it at the beginning
    handlers.splice(0, 0, handler);
};

على سبيل المثال ، بالنسبة لهذه الترميز ، ستعمل على النحو التالي ( مثال هنا ):

<div id="me">..</div>

$("#me").click(function() { alert("1"); });
$("#me").click(function() { alert("2"); });    
$("#me").bindFirst('click', function() { alert("3"); });

$("#me").click(); // alerts - 3, then 1, then 2

ومع ذلك ، بما أن .data('events') ليست جزءًا من واجهة برمجة التطبيقات العامة الخاصة بهم بقدر ما أعرف ، فقد يؤدي تحديث jQuery إلى كسر الشفرة الخاصة بك إذا تغير التمثيل الأساسي للأحداث المرفقة من مصفوفة إلى شيء آخر ، على سبيل المثال.

إخلاء المسؤولية: نظرًا لأن أي شيء ممكن :) ، فإليك الحل ، ولكنني ما زلت أخطئ في جانب إعادة بناء الشفرة الحالية ، لأن مجرد محاولة تذكر ترتيب إرفاق هذه العناصر يمكن أن يخرج عن نطاق السيطرة بينما تستمر في إضافة المزيد والمزيد من هذه الأحداث مرتبة.





jquery