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

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

إضافة لعمل قوائم منزلقة بشكل عمودي

الإضافة ستكون عبارة عن عمل قائمة منزلقة بشكل عمودي والذي عملناه في درس سابق، ولكن ستوفر الإضافة عدة طرق لظهور القائمة إضافةً طريقة ظهور محتوى القائمة عند الضغط على أحد القوائم، بهذا حددنا غرض الإضافة وإسمها إضافة moSlide على سبيل المثال، والإختيارات التي سنستعملها في الإضافة هي:

  • title: لتحديد عنصر عنوان القائمة.
  • contain: لتحديد عنصر محتوى القائمة.
  • options: إختيارات أخرى يتم كتابتها بواسطة هذه الأقواس { .. } وهي متغيرات إختيارية، ومنها سأحدد أربعة متغيرات أخرى.

عن طريق متغير options الذي سيصبح كائن يحتوي على أربعة متغيرات كإختيارات وهي:

  • type: نوع العرض الذي ستظهر به القوائم وسنستعرضه لاحقاً، نوع الإفتراضي للعرض هو normal.
  • duration: مدة ظهور العرض، القيمة الإفتراضية هي 0.
  • advanced: متغير يشير إلى طريقة أفضل من الطريقة الإفتراضية للعرض، وسننشئ أكثر من طريقة أخرى للظهور أيضاً، القيمة الإفتراضية هي 0 أي ليس هناك عرض متقدم.
  • openBox: متغير يشير إلى إبقاء جميع الصناديق مفتوحة، الخيار هذا فقط مع نوع العرض ‘Normal’.

شروط نجاح تطبيق الإضافة:

  • أن يكون عنوان القائمة ومحتواها معاً كعنصرين شقيقين Sibling elements.
  • تطبق خاصية moSlide (وهي إسم الإضافة والخاصية الرئيسية للإضافة) على عنصر أب يحوي القوائم الفرعية. (ستجد تفاصيل هذه النقطة في آخر الدرس)

أما بالنسبة أنواع العروض أو القوائم التي تمتاز بها الإضافة سيتم شرحه خلال الدرس وهذه صورة عامة لأنواع العروض وأنصحك برؤية مثال الدرس فسيوضح الفكرة أكثر.

إنشاء دالة خاصة

نبدأ بإنشاء الدالة التي ستحوي الإضافة، وستكون ما بين القوسين ( ) وتستخدم هذه الطريقة لتجنب تعارض متغيرات الإضافة مع متغيرات لإضافات أخرى أو متغيرات jQuery كما تبين الشفرة:

(function($){
     // محتوى الإضافة
})(jQuery);

إضافةً إلى إنشاء دالة جديدة بإسم moSlide وهي إسم الإضافة أيضاً ستكون الشفرة الحالية بهذا الشكل:

(function($){
     $.fn.moSlide = function(title,contain,options) {
            // محتوى الخاصية او الإضافة
     }
})(jQuery);

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

إنشاء المتغيرات

سنضع المتغيرات الإختيارية في options بعد كتابة متغير title و contain، متغيرات options ستكون عبارة عن إختيارات وسيتم كتابتها بإستخدام { } (عند تطبيق الإضافة على العنصر)، نستخدم دالة extend في إضافة المتغيرات إلى options والتي وضعتها أيضاً في آخر قائمة موجودة في الأعلى مع الشرح:

(function($){
     $.fn.moSlide = function(title,contain,options) {
         var options = jQuery.extend({
             type: 'normal',
             duration: 0,
             advanced: 0,
             openBox: 0
         },options);
     }
})(jQuery);

كما ترى لدينا متغيرات بقيم إفتراضية متغير type والذي أشرنا فيه إلى قيمة normal و متغيرات الأخرى مثل duration و advanced و openBox بقيمة صفر وسنستعملها في عملنا على الإضافة لاحقاً.

عمل الربط بين الخاصية وبقية الخواص

نأتي إلى تحديد العنصر الحالي بإستخدام دالة each لتطبيق غرض الإضافة عليه إضافةً إلى ربط خاصية الإضافة الحالية مع الخصائص الأخرى (لإمكانية كتابة الشفرة بهذه الطريقة $(Element).pluginName().next().show().anotherProperty(); ) سواء كانت خصائص jQuery أو خصائص لإضافات أخرى تطبق على نفس العنصر بجانب خاصية الإضافة، الشفرة ستكون بهذه الطريقة بعد إستخدام دالة each:

(function($){
     $.fn.moSlide = function(title,contain,options) {
         var options = jQuery.extend({
             type: 'normal',
             duration: 0,
             advanced: 0,
             openBox: 0
         },options);

         return this.each(function(){
             ..
         });
     }
})(jQuery);

كتابة عمل الإضافة

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

أبدأ أول شفرة وغرضها إظهار العنصر الرئيسي إذا كان مغلق سابقاً بإستخدام خاصية مثل hide في jQuery، والغرض الثاني هو معرفة نوع الصنف الذي يحمله العنصر هل هو نوع id أم class؟ عن طريق خاصية attr في مكتبة jQuery بعد ذلك نضع عنوان القائمة ومحتواها في متغيرين جديدين لإستخدامهم فيما بعد:


    // إظهار العنصر الحالي إذا كان مخفي
    $(this).show(0);

    // التأكد من وجود صنف من نوع id أو class
    // في متغيرات جديدة moSlide ووضع إختيارات خاصية
	if($(this).attr('id') != '') {
		var boxClass = $(this).attr('id');
		var objTitle = $('#'+boxClass+' '+title);
        var objContain = $('#'+boxClass+' '+contain);
	}
	if ($(this).attr('class') != '') {
		var boxClass = $(this).attr('class');
		var objTitle = $('.'+boxClass+' '+title);
        var objContain = $('.'+boxClass+' '+contain);
	}

نأتي بعدها لأول إختيار وهو openBox وصفناه في بداية الموضوع، بحيث إذا كانت هذه الخاصية تساوي رقم 1 بمعنى true جميع محتويات القوائم تكون مفتوحة تلقائياً (حددنا هذه الميزة فقط لنوع العرض Normal أي في الوضع العادي) ومن ثم يتم إغلاقها وفتحها مجدداً بالضغط على عنوان القائمة كما تبين هذه الشفرة:

if(options.openBox == 1) {
     objContain.show();
}

بعد ذلك سنضع عدة عروض للقوائم عبر متغير type وسأستخدم دالة switch وسيكون لمتغير type ثلاثة قيم هي normal و organized و introduced:

switch(options.type){
     case'normal':
         // normal محتوى قيمة
     break;
     case'organized':
         // organized محتوى قيمة
     break;
     case'introduced':
         // introduced محتوى قيمة
     break;
}

نوع العرض normal

نبدأ بأول قيمة وهي normal وهي القيمة الإفتراضية لخاصية moSlide الخاصة بالإضافة، بحيث يكون فتح القوائم بشكل عادي مع سرعة محددة عبر المتغير duration الذي كتبناه في قائمة المتغيرات عبر options وسيتم تطبيق إمكانية فتح القوائم تلقائياً عبر هذا الإختيار الخاص بمتغير openBox:

case'normal':
	objTitle.click(function(){
		$(this).next().slideToggle(options.duration);
	});
break;

إختصار الشفرة السابقة هي أنه إذا تم الضغط على عنوان القائمة يتم فتح وإغلاق محتواها حسب الوضع الحالي للقائمة وذلك بإستخدام خاصية next لأن محتوى القائمة يجب أن يكون بعد العنوان مباشرةً إضافةً إلى خاصية slideToggle وتحديد المدة الزمنية للظهور بأجزاء الثانية عن طريق متغير duration الذي كتبناه مع المتغيرات التي نريدها.

نوع العرض organized

نأتي إلى قيمة organized أي ستكون القائمة منظمة وسيتم تمييز القائمة المفتوحة في هذا العرض بالصنف .current الذي تستطيع تنسيقه بتقنية CSS (يمكننا وضع الصنف ضمن الإختيارات لإختيار الصنف المناسب لمستخدم الإضافة ولكن فضلت إعتمادها)، صورة للقائمة من هذا النوع:

وتم عرض الصورة من قبل التي تبين كافة أنواع العروض وأحببت أعيدها مرة أخرى، اما الشفرة الخاصة بقيمة organized:

case'organized':
	objContain.hide();
	if(options.advanced == 1) {
		objTitle.first().addClass('current');
		objTitle.first().next().show();
	}

	objTitle.click(function(){
		objTitle.removeClass('current');
        $(this).addClass('current');

        objTitle.not('.current').next().slideUp(options.duration);
        $(this).next().slideDown(options.duration);

	});
break;

في الشفرة السابقة إستخدمت متغير advanced كوضع متقدم بحيث إذا كانت قيمة هذا المتغير تساوي واحد ستكون محتوى القائمة الأولى مفتوحة، بعد ذلك نأتي إلى الحدث click مرة أخرى ولا شيء جديد هنا بحيث يتم إضافة الصنف .current إلى عنوان القائمة التي تم الضغط عليها وحدف هذا الصنف من القائمة السابقة (إذا كانت هناك قائمة سابقة تم الضغط عليها) بعد ذلك نغلق محتوى القائمة السابقة ونفتح المحتوى الخاص بهذه القائمة مع تحديد المدة بواسطة متغير duration.

نوع العرض introduced

نأتي إلى النوع الأخير والذي يحمل قيمة introduced، في هذا العرض سيتم فتح القوائم كلها بالترتيب أو بمدة زمنية واحدة، بحيث في حال كان هذا العرض متقدم بإستخدام قيمة advanced يتم فتح القوائم بالترتيب.

على سبيل المثال إذا كانت قيمة advanced تساوي واحد تفتح محتوى القائمة الأولى ثم الثانية ثم الثالثة وهكذا إلى نهاية القائمة بترتيب زمني محدد عبر قيمة المتغير duration، ستكون الشفرة الخاصة بالعرض بهذه الطريقة:

case'introduced':
	objContain.hide();
	if(options.advanced == 1) {
	   var containLength = objContain.length -1;
	   for(x=0;x<=containLength;x++){
			objContain.eq(x).delay(options.duration * x).slideDown(options.duration);
	   }
	}
	if(options.advanced != 1) {
		objContain.slideDown(options.duration);
	}

	objTitle.click(function(){
		$(this).next().slideToggle(options.duration);
	});

break;

في الشفرة السابقة نبدأ بإغلاق جميع محتويات القوائم إذا كانت مفتوحة، بعدها نتأكد من قيمة المتغير advanced أعني العرض المتقدم، إذا كان العرض الخاص بقيمة introduced متقدم نستخدم دالة التكرار for بحيث يتم فتح القائمة الدور بتحديد مدة زمنية مناسبة بعد فتح كل قائمة.

إستخدمت دالة eq لتحديد رقم القائمة التي تبدأ بصفر في المصفوفات، ثم إستخدمت دالة delay لتأخير فتح كل قائمة حسب الترتيب القائمة والمتغير x هو الأساس في حلقة التكرار بحيث لا يزيد عن عدد القوائم ويزيد مرة واحدة في كل تكرار، بعد ذلك تستخدم خاصية slideDown لفتح القائمة مع تحديد مدة زمنية محددة بإستخدام المتغير duration.

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

الشفرة كاملة

إنتهينا من كتابة الإضافة الآن وأعرض الشفرة كاملة من البداية حتى النهاية مع التعليقات على الشفرات لكي تساعد على شرح أهم أجزاء الشفرة:

(function($){
    // moSlide إسم الخاصية الرئيسية في الإضافة وهي
	$.fn.moSlide = function(title,contain,options) {
		var options = jQuery.extend({
			type: 'normal',
			duration: 0,
			advanced: 0,
			openBox: 0
		},options);

		return this.each(function(){
            // إظهار العنصر الحالي إذا كان مخفي
			$(this).show(0);

            // التأكد من وجود صنف من نوع id أو class
            // في متغيرات جديدة moSlide ووضع إختيارات خاصية
			if($(this).attr('id') != '') {
				var boxClass = $(this).attr('id');
				var objTitle = $('#'+boxClass+' '+title);
                var objContain = $('#'+boxClass+' '+contain);
			}
			if ($(this).attr('class') != '') {
				var boxClass = $(this).attr('class');
				var objTitle = $('.'+boxClass+' '+title);
                var objContain = $('.'+boxClass+' '+contain);
			}

			if(options.openBox == 1) {
				objContain.show();
			}

			switch(options.type){

            // أنواع فتح عروض فتح القوائم هي
            // normal و organized و introduced
    		case'normal':
    			objTitle.click(function(){
    				$(this).next().slideToggle(options.duration);
    			});
    		break;
    		case'organized':
    			objContain.hide();

                // تساوي واحد يكون العرض متقدم advanced إذا كانت قيمة
                // بفتح نافذة واحدة تلقائياً وهي القائمة الأولى
    			if(options.advanced == 1) {
    				objTitle.first().addClass('current');
    				objTitle.first().next().show();
    			}

    			objTitle.click(function(){
    				objTitle.removeClass('current');
                    $(this).addClass('current');

                    objTitle.not('.current').next().slideUp(options.duration);
                    $(this).next().slideDown(options.duration);

    			});
    		break;
    		case'introduced':
    			objContain.hide();

                // تساوي واحد يكون العرض متقدم advanced إذا كانت قيمة
                // بفتح القوائم بالترتيب بطريقة مقطعة
    			if(options.advanced == 1) {
    			   var containLength = objContain.length -1;
    			   for(x=0;x<=containLength;x++){
    					objContain.eq(x).delay(options.duration * x).slideDown(options.duration);
    			   }
    			}
                // فتح القائمة بشكل عادي وليس بشكل متقدم
    			if(options.advanced != 1) {
    				objContain.slideDown(options.duration);
    			}

    			objTitle.click(function(){
    				$(this).next().slideToggle(options.duration);
    			});

    		break;
		 }

		});

	}
})(jQuery);

يمكنك بعدها حفظ الشفرة بملف خاص مثل moSlide.js أو كما أسميتها jq.moSlide.js (إسم هذه الإضافة الموجودة في الدرس)

تطبيق الإضافة في jQuery

عند التعامل مع أي عنصر تريد تطبيق الإضافة عليها يكون التطبيق على عنصر أب محوي القوائم التي تريد عمل التأثير عليها، وشرط آخر هو أن يكون عنصر عنوان القائمة وعنصر محتوى القائمة معاً لا حدود تفرقهما مثل شفرة HTML هذه:

<div id="boxes">

     <div class="title"><h3>القائمة الأولى</h3></div>
     <div class="contain">
         <p>مكان القائمة أو النصوص الخاصة بالقائمة الأولى، يمكنك إضافة المزيد.</p>
     </div>

     <div class="title"><h3>القائمة الثانية</h3></div>
     <div class="contain">
         <p>مكان القائمة أو النصوص الخاصة بالقائمة الثانية، يمكنك إضافة المزيد.</p>
     </div>

     <div class="title"><h3>القائمة الثالثة</h3></div>
     <div class="contain">
         <p>مكان القائمة أو النصوص الخاصة بالقائمة الثالثة، يمكنك إضافة المزيد.</p>
     </div>

     <div class="title"><h3>القائمة الرابعة</h3></div>
     <div class="contain">
         <p>مكان القائمة أو النصوص الخاصة بالقائمة الرابعة، يمكنك إضافة المزيد.</p>
     </div>

</div>

أو يمكن أن تفصل عناصر القوائم عن بعضهم في عدة عناصر تستخدم الوسم div بهذه الطريقة:

<div id="boxes">

<div>
     <div class="title"><h3>القائمة الأولى</h3></div>
     <div class="contain">
         <p>مكان القائمة أو النصوص الخاصة بالقائمة الأولى، يمكنك إضافة المزيد.</p>
     </div>
</div>

<div>
     <div class="title"><h3>القائمة الثانية</h3></div>
     <div class="contain">
         <p>مكان القائمة أو النصوص الخاصة بالقائمة الثانية، يمكنك إضافة المزيد.</p>
     </div>
</div>

<div>
     <div class="title"><h3>القائمة الثالثة</h3></div>
     <div class="contain">
         <p>مكان القائمة أو النصوص الخاصة بالقائمة الثالثة، يمكنك إضافة المزيد.</p>
     </div>
</div>

<div>
     <div class="title"><h3>القائمة الرابعة</h3></div>
     <div class="contain">
         <p>مكان القائمة أو النصوص الخاصة بالقائمة الرابعة، يمكنك إضافة المزيد.</p>
     </div>
</div>

</div>

بالنسبة لتنسيق CSS الخاص بمثال الدرس فهو نفسه الموجود في درس القوائم المزلقة السابق الذي طرحته في المدونة، هذه الشفرة مرة أخرى مع الأجزاء المهمة وبالأخص صنف .current الذي أحتاجه لتطبيق عرض organized الخاص بالقائمة:

.title {
    margin: 1px 0 0;
    padding: 1.5px 5px 1px;
    width: 250px;
    height: 26px;
    -moz-border-radius-topleft: 7px;
    -moz-border-radius-topright: 7px;
    -moz-border-radius-bottomleft: 2px;
    -moz-border-radius-bottomright: 2px;
    -webkit-border-top-left-radius: 7px;
    -webkit-border-top-right-radius: 7px;
    -webkit-border-bottom-left-radius: 2px;
    -webkit-border-bottom-right-radius: 2px;
    background: #c5c5c5 url(box-light.png) repeat-x top;
}
.title h3 {
    margin: 0;
    padding: 4px 5px;
    font: bold 15px "Times New Roman";
    color: #353232;
}

.title.current { background-color: #5f5f5f; }
.title.current h3 a { color: #fff; }
.contain {
    margin: 1px 0 0;
    padding: 5px 5px;
    width: 248px;
    display: none;
    border: 1px solid #5f5f5f;
    -moz-border-radius: 2px;
    -webkit-border-radius: 2px;
}

.title h3 a { padding: 5px 0; color: #353232; }
.title h3 a:hover { color: #ffffff; }

.contain p { font: 13px tahoma; color: #383737; }

عند التطبيق ضمن مكتبة jQuery، يتم تطبيق الإضافة على عنصر #boxes على سبيل المثال بهذا الشكل وبتحديد صنف عنوان القائمة وهي .title و محتوى القائمة وهي .contain:


$(function(){
     $('#boxes').moSlide('.title','.contain');
});

أو يمكن إستخدام نوع العرض المنظم الذي يحمل الإسم organized مع تحديد مدة ثانية واحدة لفتح القائمة:

$(function(){
     $('#boxes').moSlide('.title','.contain', { type: 'organized', duration: 1000 });
});

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