البحث في الموقع
المحتوى عن 'إخفاء'.
-
يُعتبر مربع البحث أحد المكونات التي لا غنى عنها في أي موقع، وفي هذا الدرس سوف تتعلم كيف يمكنك إنشاء مربع بحث يظهر ويختفي عند النقر عليه. قد تعتقد أنّ القيام بذلك سيكون سهلًا جدًا ولكنك سوف ترى أنّ الأمر ليس كذلك، فسوف نحتاج إلى تطبيق بعض الخدع حتى نجعل كل شيء يعمل بشكل صحيح. هذه هي المتطلبات التي نريدها مبدئيًا: ما نريده مبدئيًا هو ظهور زر يحتوي على أيقونة بحث. عند النقر على ذلك الزر نريد أن يظهر مربع البحث. نريده أن يكون متجاوبًا مع جميع الأجهزة. عند كتابة أي شيء في مربع البحث نريد أن يقوم المربع بالقيام بالبحث فور النقر على مفتاح الدخول (enter) من لوحة المفاتيح أو النقر على الأيقونة نفسها. إذا كان مربع البحث ظاهرًا ولكنه كان فارغًا فإننا نريده أن يختفي عند النقر على أيقونة البحث (أي أننا إذا قمنا بالنقر على أيقونة البحث وكان المربع فارغًا فإننا لا نريده أن يقوم بالبحث وإنما يقوم بإخفاء مربع البحث). نريد أيضًا أن يختفي مربع البحث عند النقر خارجه سواء كان فارغًا أم لا. لو كانت الجافاسكربت معطلة لدى المستخدم فإننا نريد لمربع البحث أن يكون ظاهرًا (أي دون الحاجة إلى النقر على أيقونة البحث). نريد أيضًا أن ندعم الأجهزة التي تعمل باللمس. يمكنك معاينة النتيجة النهائية لهذا الدرس. بما أننا بتنا الآن نعرف ما نريد فلنقم بذلك. بنية ملف HTML كل ما نحتاجه من وسوم HTML هو حاوٍ رئيسي (main container) وسوف يكون عبارة عن وسم <div> وسوف نحتاج إلى وسم <form> وإلى حقلي إدخال (inputs)؛ واحد من نوع "text" والآخر من نوع "submit" وأخيرًا سوف نحتاج إلى وسم <span> ليحتوي على أيقونة البحث: <div id="sb-search" class="sb-search"> <form> <input class="sb-search-input" placeholder="Enter your search term..." type="search" value="" name="search" id="search"> <input class="sb-search-submit" type="submit" value=""> <span class="sb-icon-search"></span> </form> </div> لنبدأ الآن بتنسيق العناصر باستخدام CSS. تنسيقات CSS بناءً على المتطلبات التي ذكرناها سابقًا فإنه يجب في البداية أن يكون لدينا زر يحتوي على أيقونة بحث وباقي العناصر يجب أن تكون مخفية. دعونا الآن نتخيل ما الذي سيحصل عند تمدد مربع البحث وجعله ظاهرًا (الذي هو نفسه سيكون الحاوي الرئيسي الذي ذكرناه سابقًا). كيف نقوم بذلك؟ سوف نستخدم الخاصية overflow: hidden وتكبير العرض الخاص بالعنصر الحاوي (sb-search) يجب أن يقوم بإظهار حقل البحث. دورة تطوير واجهات المستخدم ابدأ عملك الحر بتطوير واجهات المواقع والمتاجر الإلكترونية فور انتهائك من الدورة اشترك الآن إذًا أول شيء نقوم به هو تنسيق العنصر الحاوي (sb-search) بحيث سوف نجعله يطوف إلى اليمين باستخدام الخاصية float: right ونعطيه الخاصية overflow: hidden، والعرض يجب أن يكون 60px ولكن بما أننا نريد أن يزيد العرض إلى 100% فإننا سنواجه بعض المشاكل في متصفحات iOS فهي لا تقبل التغيير من عرض يعتمد على الـpixels إلى عرض يعتمد على النسب المئوية. لذلك سوف نقوم بتعريف خاصية min-width بالقيمة 60px وخاصية width بقيمة 0%. يمكنك قراءة المزيد عن هذا الحل العبقري من خلال هذا الرابط. سوف نستعمل أيضًا الخاصية transition والخاصية webkit-backface-visibility: hidden- لتلافي بعض الآثار للحقول في متصفحات الهواتف iOS: .sb-search { position: relative; margin-top: 10px; width: 0%; min-width: 60px; height: 60px; float: right; overflow: hidden; -webkit-transition: width 0.3s; -moz-transition: width 0.3s; transition: width 0.3s; -webkit-backface-visibility: hidden; } أي شيء يتجاوز/يفيض عن هذا المربع الصغير لن يكون ظاهرًا. لنقم الآن بموضعة حقل البحث. سوف نستعمل قيمة مئوية بالنسبة للعرض حتى نسمح للحقل بأن يتمدد مع تمدد العنصر الحاوي. ومع إضافة الارتفاع (height) وحجم الخط (font-size) والـpadding المناسبة يمكننا توسيط النص داخل العنصر (استعملنا padding بدل line-height لأن الخاصية line-height لا تعمل بشكل جيد في متصفح IE8). قد يبدو استعمالنا للخاصية position: absolute غير ضروري، ولكن استعمالها يقوم بحل مشكلة تظهر عند إغلاق مربع البحث بحيث يبدو الحقل وكأنه ظاهر في الجانب الأيمن لفترة قصيرة جدًا. .sb-search-input { position: absolute; top: 0; right: 0; border: none; outline: none; background: #fff; width: 100%; height: 60px; margin: 0; z-index: 10; padding: 20px 65px 20px 20px; font-family: inherit; font-size: 20px; color: #2c3e50; } input[type="search"].sb-search-input { -webkit-appearance: none; -webkit-border-radius: 0px; } قمنا أيضًا بإزالة التنسيقات الإفتراضية لحقل البحث في متصفحات WebKit. دعونا نقوم الآن بتعريف لون الخط بالنسبة للـplaceholder (الـplaceholder هو نص يظهر داخل حقل البحث قبل أن يقوم المستخدم بكتابة أي شيء بداخله حتى يُعطي المستخدم لمحة عما يجب عليه كتابته في ذلك الحقل): .sb-search-input::-webkit-input-placeholder { color: #efb480; } .sb-search-input:-moz-placeholder { color: #efb480; } .sb-search-input::-moz-placeholder { color: #efb480; } .sb-search-input:-ms-input-placeholder { color: #efb480; } دعونا الآن نقوم بتنسيق زر أيقونة البحث وحقل التأكيد/الإرسال (submit input)، فنحن نريدهما أن يظهرا في نفس المكان لذلك يجب أن نضعهما في الجانب الأيمن ونعطيهما نفس الأبعاد. وبما أنهما سيظهران فوق بعضهما فسوف نعطيهما الخاصية position: absolute: .sb-icon-search, .sb-search-submit { width: 60px; height: 60px; display: block; position: absolute; right: 0; top: 0; padding: 0; margin: 0; line-height: 60px; text-align: center; cursor: pointer; } نريد في بداية الأمر أن تكون الأيقونة قابلة للنقر، وعند فتح حقل البحث فإننا نريد لحقل التأكيد/الإرسال (submit input) أن يكون قابلًا للنقر. لذلك سوف نُعطي حقل التأكيد الخاصية z-index: -1 ونجعله شفافًا/مخفيًا حتى يمكننا رؤية أيقونة البحث: .sb-search-submit { background: #fff; /* IE needs this */ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; /* IE 8 */ filter: alpha(opacity=0); /* IE 5-7 */ opacity: 0; color: transparent; border: none; outline: none; z-index: -1; } لاحظ أننا لم نقم بجعل الخلفية شفافة وذلك لأن الأمر لا يعمل بشكل جيد في متصفح IE فالعنصر لا يكون قابلًا للنقر عند ذلك، لذلك قمنا باستعمال لون معين (الأبيض) وجعلنا الشفافية (opacity) تساوي صفر. وبما أننا نريد لأيقونة البحث أن تظهر فوق كل شيء فإننا سوف نقوم بإعطائها الخاصية z-index بقيمة عالية، وسوف نقوم باستخدام الفئة الزائفة ::before لإضافة الأيقونة: .sb-icon-search { color: #fff; background: #e67e22; z-index: 90; font-size: 22px; font-family: 'icomoon'; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; -webkit-font-smoothing: antialiased; } .sb-icon-search:before { content: "\e000"; } ودعونا لا ننسى إضافة خط الويب (web font) في بداية ملف الـCSS: /* Search icon by IcoMoon, made with http://icomoon.io/app/ */ @font-face { font-family: 'icomoon'; src:url('../fonts/icomoon/icomoon.eot'); src:url('../fonts/icomoon/icomoon.eot?#iefix') format('embedded-opentype'), url('../fonts/icomoon/icomoon.woff') format('woff'), url('../fonts/icomoon/icomoon.ttf') format('truetype'), url('../fonts/icomoon/icomoon.svg#icomoon') format('svg'); font-weight: normal; font-style: normal; } يمكننا الآن إعطاء العنصر الحاوي (sb-search) عرضًا بقيمة 100% وذلك في حالتين؛ الأولى عند النقر على أيقونة البحث وذلك عن طريق إضافة فئة (class) بالاسم "sb-search-open" باستخدام الجافاسكربت والثانية عندما لا يكون الجافاسكربت مفعّلًا لدى المستخدم: .sb-search.sb-search-open, .no-js .sb-search { width: 100%; } لنقم الآن بتغيير لون الخط الخاص بعنصر أيقونة البحث ونضعه أسفل حقل التأكيد/الإرسال وذلك عن طريق إعطائه قيمة z-index أقل من 90 (وهي القيمة التي أعطيناها للعنصر "sb-icon-search"): .sb-search.sb-search-open .sb-icon-search, .no-js .sb-search .sb-icon-search { background: #da6d0d; color: #fff; z-index: 11; } وأخيرًا لنقم بإضافة الخاصية z-index لحقل التأكيد/الإرسال ولكن بقيمة أكبر من 11 حتى يمكننا النقر عليها: .sb-search.sb-search-open .sb-search-submit, .no-js .sb-search .sb-search-submit { z-index: 90; } انتهينا الآن من تنسيقات CSS وبقي علينا الجافاسكربت. بعض الجافاسكربت لنبدأ بإضافة وإزالة الفئة "sb-search-open". بحيث سوف يتم إضافة الفئة عند النقر على الحاوي الرئيسي (sb-search) وإزالته عند النقر على حقل التأكيد/الإرسال ولكن فقط إذا كان حقل البحث فارغًا، أمّا إذا لم يكن فارغًا فإننا نريد تأكيد عملية البحث. وحتى لا نقوم بإزالة الفئة عند النقر على الحقل (لأن الحاوي بأكمله قابل للنقر) فإننا نحتاج إلى منع حدث النقر (click event) من الانتشار على ذلك العنصر. هذا يعني أنّ النقر على الحقل لن يؤدي إلى إثارة حدث النقر على العناصر الحاوية له. ;( function( window ) { function UISearch( el, options ) { this.el = el; this.inputEl = el.querySelector( 'form > input.sb-search-input' ); this._initEvents(); } UISearch.prototype = { _initEvents : function() { var self = this, initSearchFn = function( ev ) { if( !classie.has( self.el, 'sb-search-open' ) ) { ev.preventDefault(); self.open(); } else if( classie.has( self.el, 'sb-search-open' ) && /^\s*$/.test( self.inputEl.value ) ) { self.close(); } } this.el.addEventListener( 'click', initSearchFn ); this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); }); }, open : function() { classie.add( this.el, 'sb-search-open' ); }, close : function() { classie.remove( this.el, 'sb-search-open' ); } } window.UISearch = UISearch; } )( window ); سوف نحتاج أيضًا إلى إضافة الأحداث التي تقوم بإزالة الفئة "sb-search-open" عند النقر خارج مربع البحث، وحتى يعمل ذلك فإننا نريد أن نتعامل مع انتشار الأحداث (event bubbling) عند النقر على الحاوي الرئيسي. ;( function( window ) { function UISearch( el, options ) { this.el = el; this.inputEl = el.querySelector( 'form > input.sb-search-input' ); this._initEvents(); } UISearch.prototype = { _initEvents : function() { var self = this, initSearchFn = function( ev ) { ev.stopPropagation(); if( !classie.has( self.el, 'sb-search-open' ) ) { ev.preventDefault(); self.open(); } else if( classie.has( self.el, 'sb-search-open' ) && /^\s*$/.test( self.inputEl.value ) ) { self.close(); } } this.el.addEventListener( 'click', initSearchFn ); this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); }); }, open : function() { var self = this; classie.add( this.el, 'sb-search-open' ); // close the search input if body is clicked var bodyFn = function( ev ) { self.close(); this.removeEventListener( 'click', bodyFn ); }; document.addEventListener( 'click', bodyFn ); }, close : function() { classie.remove( this.el, 'sb-search-open' ); } } window.UISearch = UISearch; } )( window ); وشيء آخر علينا الاهتمام به وهو قصّ مصطلح البحث. عندما نقوم أيضًا بالنقر على أيقونة البحث فإننا نريد أن يكون الحقل مفعّلًا (focused)، ولأنّ هذا يسبب بعض المشاكل في متصفح iOS (لوحة المفاتيح تظهر في نفس الوقت) فإننا نريد تلافي ذلك في هذه الحالة، وعندما يتم إغلاق مربع البحث فإننا نريد أن يكون مربع البحث غير مفعل (blur). هذا سوف يحل بعض المشاكل في بعض الأجهزة التي تُظهر بأنّ المؤشر يومض حتى بعد أن يكون الحقل مغلقًا. ملاحظة جانبية: كلمة focus تدل على أنّ مربع البحث مفعّل (أي أنّ المؤشر بداخله) وكلمة blur تدل على عكس ذلك. ;( function( window ) { // http://stackoverflow.com/a/11381730/989439 function mobilecheck() { var check = false; (function(a){if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); return check; } // http://www.jonathantneal.com/blog/polyfills-and-prototypes/ !String.prototype.trim && (String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }); function UISearch( el, options ) { this.el = el; this.inputEl = el.querySelector( 'form > input.sb-search-input' ); this._initEvents(); } UISearch.prototype = { _initEvents : function() { var self = this, initSearchFn = function( ev ) { ev.stopPropagation(); // trim its value self.inputEl.value = self.inputEl.value.trim(); if( !classie.has( self.el, 'sb-search-open' ) ) { ev.preventDefault(); self.open(); } else if( classie.has( self.el, 'sb-search-open' ) && /^\s*$/.test( self.inputEl.value ) ) { self.close(); } } this.el.addEventListener( 'click', initSearchFn ); this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); }); }, open : function() { var self = this; classie.add( this.el, 'sb-search-open' ); // focus the input if( !mobilecheck() ) { this.inputEl.focus(); } // close the search input if body is clicked var bodyFn = function( ev ) { self.close(); this.removeEventListener( 'click', bodyFn ); }; document.addEventListener( 'click', bodyFn ); }, close : function() { this.inputEl.blur(); classie.remove( this.el, 'sb-search-open' ); } } window.UISearch = UISearch; } )( window ); وحتى يعمل كل شيء بشكل سلس في أجهزة الهواتف فإننا سوف نحتاج إلى إضافة أحداث اللمس (touch events). كما أنّ إضافة preventDefault في دالّة initSearchFn سوف يمنع حدث النقر واللمس من أن يتفعّلا مع بعضهما في أجهزة اللمس. ;( function( window ) { // http://stackoverflow.com/a/11381730/989439 function mobilecheck() { var check = false; (function(a){if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); return check; } // http://www.jonathantneal.com/blog/polyfills-and-prototypes/ !String.prototype.trim && (String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }); function UISearch( el, options ) { this.el = el; this.inputEl = el.querySelector( 'form > input.sb-search-input' ); this._initEvents(); } UISearch.prototype = { _initEvents : function() { var self = this, initSearchFn = function( ev ) { ev.stopPropagation(); // trim its value self.inputEl.value = self.inputEl.value.trim(); if( !classie.has( self.el, 'sb-search-open' ) ) { ev.preventDefault(); self.open(); } else if( classie.has( self.el, 'sb-search-open' ) && /^\s*$/.test( self.inputEl.value ) ) { ev.preventDefault(); self.close(); } } this.el.addEventListener( 'click', initSearchFn ); this.el.addEventListener( 'touchstart', initSearchFn ); this.inputEl.addEventListener( 'click', function( ev ) { ev.stopPropagation(); }); this.inputEl.addEventListener( 'touchstart', function( ev ) { ev.stopPropagation(); } ); }, open : function() { var self = this; classie.add( this.el, 'sb-search-open' ); // focus the input if( !mobilecheck() ) { this.inputEl.focus(); } // close the search input if body is clicked var bodyFn = function( ev ) { self.close(); this.removeEventListener( 'click', bodyFn ); this.removeEventListener( 'touchstart', bodyFn ); }; document.addEventListener( 'click', bodyFn ); document.addEventListener( 'touchstart', bodyFn ); }, close : function() { this.inputEl.blur(); classie.remove( this.el, 'sb-search-open' ); } } window.UISearch = UISearch; } )( window ); وأخيرًا، للأجهزة التي لا تدعم addEventListener وremoveEventListener فإننا سوف نستعمل polyfill. // EventListener | @jon_neal | //github.com/jonathantneal/EventListener !window.addEventListener && window.Element && (function () { function addToPrototype(name, method) { Window.prototype[name] = HTMLDocument.prototype[name] = Element.prototype[name] = method; } var registry = []; addToPrototype("addEventListener", function (type, listener) { var target = this; registry.unshift({ __listener: function (event) { event.currentTarget = target; event.pageX = event.clientX + document.documentElement.scrollLeft; event.pageY = event.clientY + document.documentElement.scrollTop; event.preventDefault = function () { event.returnValue = false }; event.relatedTarget = event.fromElement || null; event.stopPropagation = function () { event.cancelBubble = true }; event.relatedTarget = event.fromElement || null; event.target = event.srcElement || target; event.timeStamp = +new Date; listener.call(target, event); }, listener: listener, target: target, type: type }); this.attachEvent("on" + type, registry[0].__listener); }); addToPrototype("removeEventListener", function (type, listener) { for (var index = 0, length = registry.length; index < length; ++index) { if (registry[index].target == this && registry[index].type == type && registry[index].listener == listener) { return this.detachEvent("on" + type, registry.splice(index, 1)[0].__listener); } } }); addToPrototype("dispatchEvent", function (eventObject) { try { return this.fireEvent("on" + eventObject.type, eventObject); } catch (error) { for (var index = 0, length = registry.length; index < length; ++index) { if (registry[index].target == this && registry[index].type == eventObject.type) { registry[index].call(this, eventObject); } } } }); })(); خاتمة هذا كان كل شيء فيما يخص هذا الدرس، أتمنى أن تكون قد استفدت منه وتعلمت منه شيئًا جديدًا. ترجمة -وبتصرّف- للمقال Expanding Search Bar Deconstructed لصاحبته Mary Lou.
-
يُساهم إضافة بعض التحريك (animation) عندما تتغيّر أجزاء صفحة الويب، في مُساعدة المُستخدِم على إدراك ما الّذي يحدث في الصّفحة، فيُمكن للتحريك أنّ يَدل على وصول مُحتوى جديد، أو لفت نظره إلى المُحتوى الّذي هو طور الإزالة. ستتناول هذه المقالة كيف يُمكن للتحريك المُساعدة على تقديم مُحتوى جديد، وذلك عبر إظهار وإخفاء عناصر قائمة من القائمة الخاصّة بهم. See the Pen WvjoXa by Walid (@01walid) on CodePen.
-
- 1
-
- إظهار
- تأثيرات حركية
-
(و 3 أكثر)
موسوم في:
-
مواقع الويب الحديثة أصبحت أكثر ديناميكية مؤخرا، عبر استخدام Javascript لتزويد المستخدم بتحديثات مباشرة ومتحكمات أكثر سلاسة. أحد أكثر المميزات شيوعا هي إظهار وإخفاء المحتوى، سواءا كان على شكل تبويبات، مخفيا على جانب الصفحة، أو على شكل قائمة متعددة المستويات. عادة، هذه المميزات يتم تطبيقها عبر Javascript فحسب، ولكنك تستطيع القيام بنفس التأثيرات عبر CSS3 بدون اللجوء إلى Javascript. في هذه المقالة سنرى كيف يمكننا تحقيق ذلك، مع تضمين بعض الأمثلة. المحتوى مثال بسيط حل حقيقي الإخفاء والإظهار عبر CSS تحسين القائمة دعم المتصفحات مثال بسيط هذا مثال بسيط يظهر كيف يمكن إخفاء وإظهار المحتوى عبر CSS: See the Pen &amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io/assassinateur/pen/YXKgRz/' href='http://codepen.io/assassinateur/pen/YXKgRz/'&amp;amp;amp;amp;amp;gt;YXKgRz&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt; by Nadir (&amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io/assassinateur' href='http://codepen.io/assassinateur'&amp;amp;amp;amp;amp;gt;@assassinateur&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt;) on &amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io' href='http://codepen.io'&amp;amp;amp;amp;amp;gt;CodePen&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt;. عندما نضغط على زر إخفاء، سيختفي الزر إلى جانب القائمة في الأسفل، ويظهر زر "إظهار". وإذا شاهدت نسخة الطباعة من الصفحة، فستلاحظ أنّ الأزرار لن تظهر. الآن بعد مشاهدتنا للمثال، لنلقي نظرة على ما استخدمنا والتقنيات الكامنة خلف ذلك، ونبني قائمة بينما نحن نقوم بذلك. حل حقيقي نستطيع استخدام الحل الذي سنطوره في إحدى الحالات التالية: نريد أن تجعل محتوى معين يظهر ويختفي في مكان معين (مثل قوائم المحتوى في ويكيبيديا) لسبب ما، قد لا نريد استعمال جافاسكربت لنبدأ بمشاهدة حل يستخدم جافاسكربت؛ قسم المحتوى في مقالة ما في ويكيبديا See the Pen &amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io/assassinateur/pen/rVBbMR/' href='http://codepen.io/assassinateur/pen/rVBbMR/'&amp;amp;amp;amp;amp;gt;rVBbMR&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt; by Nadir (&amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io/assassinateur' href='http://codepen.io/assassinateur'&amp;amp;amp;amp;amp;gt;@assassinateur&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt;) on &amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io' href='http://codepen.io'&amp;amp;amp;amp;amp;gt;CodePen&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt;. المثال السابق مأخوذ من صفحة ويكيبيديا (هو مجرد HTML رغم هذا)، يعمل الجدول عبر إخفاء المحتوى، وتغيير الكلمة من "أخفي" إلى "أظهر" ويتم هذا عبر جافاسكربت. يتم إضافة تسجيل مراقب للزر (Event Listener)، عندما يتم الضغط على الزر، يقوم jQuery بإضافة class جديد للصندوق، يتم أيضا إَضافة واحد لحاوي الروابط، كما يتم تغيير محتوى الزر من "أخفي" إلى "أظهر". الإخفاء والإظهار عبر CSS سنحاول إعادة صنع قائمة ويكيبيديا، ولكن عبر CSS فحسب، فسنفقد بعض المميزات، ولكننا سنكسب بعض الأمور في نفس الوقت. يستطيع رابط ما أن يأخذك لأيّ جزء من الصفحة عبر تحديد ID الخاص به كمكان اتجاه للرابط. في المثال سنستخدم شيئا مشابها، ولكن ليس للتصفح، بل لإظهار وإخفاء المحتوى. نستخدم الكود في الأسفل ثم نشرحه. See the Pen &amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io/assassinateur/pen/oXvOZb/' href='http://codepen.io/assassinateur/pen/oXvOZb/'&amp;amp;amp;amp;amp;gt;oXvOZb&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt; by Nadir (&amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io/assassinateur' href='http://codepen.io/assassinateur'&amp;amp;amp;amp;amp;gt;@assassinateur&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt;) on &amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io' href='http://codepen.io'&amp;amp;amp;amp;amp;gt;CodePen&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt;. مثل قائمة ويكيبديا، نريد من قائمتنا أن تظهر مفتوحة بشكل افتراضي. نصنع class باسم .show و .hide. وبما أن القائمة ستكون مفتوحة، لا نريد من زر .show أن يظهر. .show { display: none; } والآن نريد أن نجعل زر الإظهار 'يظهر' ونخفي زر 'الإخفاء' عندما يتم الضغط عليه، يمكننا تحقيق هذا عبر شبه الصنف :focus والذي يعمل عندما يتم الضغط على الرابط بواسطة الفأرة أو التركيز عليه بواسطة لوحة المفاتيح. سنستخدم المحدد "+" والذي يعني العنصر الذي يأتي مباشرة بعد العنصر الأول. a.hide:focus + .show { display: inline; } a.hide:focus { display: none; } الآن، لنجعل الزر يعمل حقا! سوف نقوم بجعل القائمة #menu تختفي عندما يكون زر الإخفاء في حالة :focus بما أنّ القائمة لا تأتي مباشرة بعد الزر، سيكون علينا استخدام المحدد غير المباشر ~ من أجل تحديد القائمة. a.hide:focus ~ #menu { display:none; } والآن، لتعديل بسيط أخير. نريد أن نخفي أزرار الإخفاء والإظهار في حال طباعة الصفحة، أسرع طريقة للقيام بالأمر هي عبر استخدام media query والتي تتفقد هل يتم طباعة الصفحة، وإن كان كذلك تقوم بإخفاء الأزرار. @media print { .hide, .show { display: none; } } حتى الآن، ما قمنا به يعطينا التأثير المطلوب الذي رأيناه في أول المقال. القائمة التي لدينا تقوم بنفس عمل قائمة ويكيبيديا تماما، عدى بعض التأثيرات الخفيفة التي سنصل لها بعد قليل. يجب التنويه على أننا لم نستخدم جافاسكربت فحسب، بل استخدمنا مقدارا ضئيلا جدا من CSS من أجل تحقيق هذا، مقارنة ب3 دوال في جافاسكربت وحوالي 20 سطرا من الكود. تحسين القائمة الآن بعدما قمنا بصنع التأثير إخفاء المحتوى، نريد أن نضيف له بعض التأثيرات البصرية، وسنستخدم CSS أيضا. الاختفاء البطيء بدلا من الإظهار والإخفاء السريع، سيكون من اللطيف لو اختفت القائمة بشكل بطيء، وسنقوم بهذا عبر transition في CSS. سوف نحدد الوضع الافتراضي للقائمة (وهي أن تظهر بشكل كامل) ثم نخفيها ببطيء. #menu { opacity: 1; } a.hide:focus ~ #menu { opacity: 0; } بدل أن نضع التأثير خصيصا للعنصر، نستطيع إَضافته لأي عنصر عبر وضعه في class مستقل .tran1 { -o-transition: all 1s; -moz-transition: all 1s; -webkit-transition: all 1s; transition: all 1s; } الآن، تستطيع إضافة التأثير لأي عنصر تريد وذلك عبر إضافة class فحسب. النتيجة النهائية بعد إَضافة بعض التحسينات البسيطة، هذه هي النتيجة النهائية See the Pen &amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io/assassinateur/pen/pJzBWQ/' href='http://codepen.io/assassinateur/pen/pJzBWQ/'&amp;amp;amp;amp;amp;gt;pJzBWQ&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt; by Nadir (&amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io/assassinateur' href='http://codepen.io/assassinateur'&amp;amp;amp;amp;amp;gt;@assassinateur&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt;) on &amp;amp;amp;amp;amp;lt;a data-cke-saved-href='http://codepen.io' href='http://codepen.io'&amp;amp;amp;amp;amp;gt;CodePen&amp;amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;amp;gt;. ملاحظة: حالما تضغط على شيء آخر، يعود كل شيء كما كان وهذا هو التصرف الطبيعي لشبه الصنف :focus ولكنه ليس الأمر الذي نريده نحن. هذه المشكلة ليست موجودة في مثال ويكيبيديا بكل تأكيد، كما أن ويكيبيديا تستخدم الـcookies لتتذكر آخر حالة للقائمة حتى بعد تحديث الصفحة، وهو شيء لا نستطيع تحقيقه للأسف. خاتمة استطعنا القيام ببعض التأثيرات البصرية باستخدام CSS لوحدها عبر تعلم كيفية إخفاء وإظهار العناصر، هناك محددات واعدة أكثر ستظهر مستقبلا، وستتيح لنا التحكم أكثر وأكثر في العناصر وتحديدها بسهولة، احدى الخدع التي يمكن استخدامها هي استخدام checkbox بدل الروابط من أجل تغيير حالة الصندوق، ولكن كان هذا ليجعل الأمر أكثر تعقيدا. حقوق الصورة البارزة محفوظة لـ Freepik
-
- css3
- javascript
-
(و 2 أكثر)
موسوم في: