CSS और जावास्क्रिप्ट के साथ हॉरिजॉन्टल समयरेखा तैयार करना
Hindi (हिंदी) translation by Ashish Rampal (you can also view the original English article)
पिछली पोस्ट में, मैंने आपको दिखाया कि स्क्रैच से एक रेस्पॉन्सिव वर्टीकल समयरेखा को कैसे बनाया जाए। आज, मैं संबंधित हॉरिजॉन्टल समयरेखा बनाने की प्रक्रिया को कवर करूंगा।
हमेशा की तरह, हम क्या निर्माण करेंगे इसकी प्रारंभिक जानकारी प्राप्त करने के लिए, संबंधित कोडपेन डेमो पर एक नज़र डालें (बेहतर अनुभव के लिए larger version देखें):
हमारे पास कवर करने के लिए बहुत कुछ है, तो चलो शुरू करें!
1. HTML मार्कअप
मार्कअप हम तीन छोटी चीज़ों के अलावा वर्टीकल समयरेखा के लिए डिफाइंड मार्कअप के समान है:
- हम एक आर्डर लिस्ट के बजाय एक अनआर्डर लिस्ट का उपयोग करते हैं, क्योंकि यह अधिक सेमैंटिकाली सही है।
- एक अतिरिक्त लिस्ट आइटम (अंतिम) है जो खाली है। आगामी सेक्शन में, हम इस कारण के बारे में चर्चा करेंगे।
- एक अतिरिक्त एलिमेंट (यानी
.arrows
) है जो टाइमलाइन नेविगेशन के लिए ज़िम्मेदार है।
यहां आवश्यक मार्कअप है:
<section class="timeline"> <ol> <li> <div> <time>1934</time> Some content here </div> </li> <!-- more list items here --> <li></li> </ol> <div class="arrows"> <button class="arrow arrow__prev disabled" disabled> <img src="arrow_prev.svg" alt="prev timeline arrow"> </button> <button class="arrow arrow__next"> <img src="arrow_next.svg" alt="next timeline arrow"> </button> </div> </section>
समयरेखा की प्रारंभिक अवस्था इस तरह दिखती है:
2. प्रारंभिक CSS स्टाइल्स जोड़ना
कुछ बुनियादी फ़ॉन्ट स्टाइल्स, कलर स्टाइल्स, आदि जिन्हे मैंने सादगी के लिए यहां छोड़ दिया है, के बाद हम कुछ संरचनात्मक CSS रूल्स को उल्लेखित करते हैं:
.timeline { white-space: nowrap; overflow-x: hidden; } .timeline ol { font-size: 0; width: 100vw; padding: 250px 0; transition: all 1s; } .timeline ol li { position: relative; display: inline-block; list-style-type: none; width: 160px; height: 3px; background: #fff; } .timeline ol li:last-child { width: 280px; } .timeline ol li:not(:first-child) { margin-left: 14px; } .timeline ol li:not(:last-child)::after { content: ''; position: absolute; top: 50%; left: calc(100% + 1px); bottom: 0; width: 12px; height: 12px; transform: translateY(-50%); border-radius: 50%; background: #F45B69; }
सबसे महत्वपूर्ण बात यह है कि, आप दो चीजों पर गौर करेंगे:
- हम लिस्ट में बड़ा टॉप और बॉटम पैडिंग को असाइन करते हैं। फिर, हम अगले भाग में समझाएंगे कि ऐसा क्यों होता है।
- जैसा कि आप निम्न डेमो में नोटिस करेंगे, इस समय हम सभी लिस्ट आइटम को नहीं देख सकते क्योंकि लिस्ट में
width: 100vw
और उसके पैरेंट काoverflow-x: hidden
है। यह प्रभावी रूप से लिस्ट आइटम्स को "masks" देगा। टाइमलाइन नेविगेशन को धन्यवाद, हालांकि, हम बाद में आइटम्स के माध्यम से नेविगेट करने में सक्षम होंगे।
इन नियमों के साथ, यह समयरेखा की वर्तमान स्थिति है (किसी भी वास्तविक कंटेंट के बिना, चीजों को स्पष्ट रखने के लिए):
3. टाइमलाइन एलिमेंट स्टाइल्स
इस बिंदु पर हम div
एलिमेंट्स को स्टाइल देंगे (हम उन्हें अब से "समयरेखा एलिमेंट्स" कहेंगे) जो कि लिस्ट आइटम का हिस्सा हैं और साथ ही उनके ::before
pseudo-elements का भी।
इसके अतिरिक्त, हम :nth-child(odd)
और :nth-child(even)
CSS pseudo-classes का उपयोग करेंगे odd और even divs के लिए styles को अलग करने के लिए।
यहां समयरेखा एलिमेंट्स के लिए सामान्य स्टाइल्स हैं:
.timeline ol li div { position: absolute; left: calc(100% + 7px); width: 280px; padding: 15px; font-size: 1rem; white-space: normal; color: black; background: white; } .timeline ol li div::before { content: ''; position: absolute; top: 100%; left: 0; width: 0; height: 0; border-style: solid; }
फिर odd के लिए कुछ स्टाइल्स:
.timeline ol li:nth-child(odd) div { top: -16px; transform: translateY(-100%); } .timeline ol li:nth-child(odd) div::before { top: 100%; border-width: 8px 8px 0 0; border-color: white transparent transparent transparent; }
और अंत में even के लिए कुछ स्टाइल्स:
.timeline ol li:nth-child(even) div { top: calc(100% + 16px); } .timeline ol li:nth-child(even) div::before { top: -8px; border-width: 8px 0 0 8px; border-color: transparent transparent transparent white; }
कंटेंट के साथ फिर से जोड़ा गया समयरेखा का नया स्टेट यहाँ है:
जैसा कि आपने शायद देखा है, समयरेखा एलिमेंट्स पूरी तरह से तैनात हैं। इसका मतलब है कि वे सामान्य डॉक्यूमेंट फ्लो से निकाल दिए जाते हैं। इसे ध्यान में रखते हुए, यह सुनिश्चित करें कि पूरी टाइमलाइन दिखाई देती हो, हमें लिस्ट के लिए लार्ज टॉप और बॉटम पैडिंग वैल्यूज को सेट करना होगा। यदि हम किसी भी प्रकार की पेडिंग अप्लाई नहीं करते हैं, तो समयरेखा क्रॉप हो जाएगी:



4. टाइमलाइन नेविगेशन स्टाइल्स
अब नेविगेशन बटन को स्टाइल करने का समय है। याद रखें कि डिफ़ॉल्ट रूप से हम पिछले एरो को निष्क्रिय कर देते हैं और इसे disabled
क्लास देते हैं।
यहां सम्बंधित CSS स्टाइल्स हैं:
.timeline .arrows { display: flex; justify-content: center; margin-bottom: 20px; } .timeline .arrows .arrow__prev { margin-right: 20px; } .timeline .disabled { opacity: .5; } .timeline .arrows img { width: 45px; height: 45px; }
ऊपर दिए गए नियम हमें यह समयरेखा देते हैं:
5. इंट्रक्टिविटी (interactivity) को जोड़ना
समयरेखा की बुनियादी संरचना तैयार है। चलो इसमें कुछ इंट्रक्टिविटी जोड़ते हैं!
वेरिएबल्स
पहली चीज़ें पहले, हमने वेरिएबल्स का एक गुच्छा लगाया है जो हम बाद में उपयोग करेंगे।
const timeline = document.querySelector(".timeline ol"), elH = document.querySelectorAll(".timeline li > div"), arrows = document.querySelectorAll(".timeline .arrows .arrow"), arrowPrev = document.querySelector(".timeline .arrows .arrow__prev"), arrowNext = document.querySelector(".timeline .arrows .arrow__next"), firstItem = document.querySelector(".timeline li:first-child"), lastItem = document.querySelector(".timeline li:last-child"), xScrolling = 280, disabledClass = "disabled";
चीजों को प्रारंभ करना
जब सभी पेज एसेट्स तैयार होते है, तो init
फ़ंक्शन को कॉल किया जाता है।
window.addEventListener("load", init);
यह फ़ंक्शन चार सब-फ़ंक्शन चलाता है:
function init() { setEqualHeights(elH); animateTl(xScrolling, arrows, timeline); setSwipeFn(timeline, arrowPrev, arrowNext); setKeyboardFn(arrowPrev, arrowNext); }
जैसे की हम एक पल में देखेंगे, इन फंक्शन्स में से प्रत्येक एक निश्चित कार्य पूरा करता है।
समान-ऊँचाई समयरेखा एलिमेंट्स
यदि आप पिछले डेमो पर वापस कूदते हैं, तो आप देखेंगे कि समयरेखा एलिमेंट्स की समान ऊंचाई नहीं है। यह हमारी समयरेखा की मुख्य फंक्शनलिटी को प्रभावित नहीं करता है, लेकिन यदि आप सभी एलिमेंट्स की समान ऊंचाई रखते हैं, तो आपको यह पसंद आएगा। इसे प्राप्त करने के लिए, हम उन्हें CSS (आसान समाधान) या एक डायनामिक हाइट के माध्यम से एक निश्चित हाइट दे सकते हैं जो जावास्क्रिप्ट के माध्यम से सबसे ऊँचे एलिमेंट की हाइट से मेल खाती है।
दूसरा विकल्प अधिक लचीला और स्थिर है, इसलिए यह एक ऐसा कार्य है जो इस व्यवहार को लागू करता है:
function setEqualHeights(el) { let counter = 0; for (let i = 0; i < el.length; i++) { const singleHeight = el[i].offsetHeight; if (counter < singleHeight) { counter = singleHeight; } } for (let i = 0; i < el.length; i++) { el[i].style.height = `${counter}px`; } }
यह फ़ंक्शन सबसे लम्बे समयरेखा एलिमेंट की हाइट को प्राप्त करता है और इसे सभी एलिमेंट्स के लिए डिफ़ॉल्ट हाइट के रूप में सेट करता है।
डेमो कैसा दिख रहा है यहां दिखाया गया है:
6. समयरेखा एनिमेटिंग
अब समयरेखा एनीमेशन पर ध्यान दें। हम ऐसे फंक्शन का निर्माण करेंगे जो यह बिहेवियर स्टेप-बाय-स्टेप लागू करता है।
सबसे पहले, हम टाइमलाइन बटन के लिए क्लिक ईवेंट लिस्टनर (event listener) को रजिस्टर करते हैं:
function animateTl(scrolling, el, tl) { for (let i = 0; i < el.length; i++) { el[i].addEventListener("click", function() { // code here }); } }
हर बार जब बटन क्लिक किया जाता है, हम टाइमलाइन बटनों की डिसेबल्ड स्टेट की जांच करते हैं और यदि वे डिसेबल्ड नहीं हैं, तो हम उन्हें डिसएबल कर देते हैं। यह सुनिश्चित करता है कि एनीमेशन समाप्त होने तक दोनों बटन को एक बार ही क्लिक किया जाएगा।
इसलिए, कोड के संदर्भ में, क्लिक हैंडलर में शुरू में ये लाइन्स शामिल हैं:
if (!arrowPrev.disabled) { arrowPrev.disabled = true; } if (!arrowNext.disabled) { arrowNext.disabled = true; }
अगले चरण निम्नानुसार हैं:
- हम चेक करते हैं कि यदि यह पहली बार है जब हमने एक बटन पर क्लिक किया है। दोबारा, ध्यान रखें कि previous बटन को डिफ़ॉल्ट रूप से डिसेबल्ड किया गया है, इसलिए केवल एक बटन जिसे प्रारंभ में क्लिक किया जा सकता है next है।
- यदि वास्तव में यह पहली बार है, तो हम टाइमलाइन को दांयीं ओर 280px स्थानांतरित करने के लिए
transform
प्रॉपर्टी का उपयोग करते हैं।xScrolling
वैरिएबल का मान मूवमेंट की मात्रा निर्धारित करता है। - इसके विपरीत, यदि हम पहले से एक बटन पर क्लिक चुके हैं, तो हम समयरेखा करंट
transform
वैल्यू को प्राप्त करते हैं और उस वैल्यू को ऐड या रिमूव के लिए, वांछित मूवमेंट (यानी 280px)। इसलिए, जब तक हम previous बटन पर क्लिक करते हैं, तब तकtransform
property की वैल्यू कम हो जाता है और समयरेखा बाएं से दाएं ले जाती है। हालांकि, जब next बटन पर क्लिक किया जाता है,transform
प्रॉपर्टी की वैल्यू बढ़ती है और टाइमलाइन को दाएं से बाएं स्थानांतरित किया जाता है।
इस फंक्शनलिटी को इम्प्लीमेंट करने वाले कोड निम्नानुसार है:
let counter = 0; for (let i = 0; i < el.length; i++) { el[i].addEventListener("click", function() { // other code here const sign = (this.classList.contains("arrow__prev")) ? "" : "-"; if (counter === 0) { tl.style.transform = `translateX(-${scrolling}px)`; } else { const tlStyle = getComputedStyle(tl); // add more browser prefixes if needed here const tlTransform = tlStyle.getPropertyValue("-webkit-transform") || tlStyle.getPropertyValue("transform"); const values = parseInt(tlTransform.split(",")[4]) + parseInt(`${sign}${scrolling}`); tl.style.transform = `translateX(${values}px)`; } counter++; }); }
अच्छा काम! हमने समयरेखा को एनिमेट करने का एक तरीका डिफाइन किया है। अगली चुनौती यह है कि यह एनीमेशन कब रुकना चाहिए। यह हमारी एप्रोच है:
- जब पहला समयरेखा एलिमेंट पूरी तरह से दिखाई देता है, इसका मतलब है कि हम पहले से ही समयरेखा की शुरुआत में पहुंच गए हैं, और इस तरह हम previous बटन को डिसएबल करते हैं। हम यह भी सुनिश्चित करते हैं कि next बटन इनेबल है।
- जब अंतिम एलिमेंट पूरी तरह से दिखाई देता है, इसका मतलब है कि हम पहले से ही समयरेखा के अंत तक पहुंच चुके हैं, और इस प्रकार हम next बटन को डिसएबल करते हैं। इसलिए हम यह भी सुनिश्चित करते हैं कि previous बटन को इनेबल किया गया है।
याद रखें कि अंतिम एलिमेंट समयरेखा एलिमेंट्स की विड्थ के बराबर विड्थ वाला एक खाली स्थान है (यानी 280px)। हम इसे यह वैल्यू (या उच्चतर) देते हैं क्योंकि हम यह सुनिश्चित करना चाहते हैं कि next बटन को डिसएबल करने से पहले अंतिम टाइमलाइन एलिमेंट दिखाई देगा।
यह पता लगाने के लिए कि टारगेट एलिमेंट्स वर्तमान व्यूपोर्ट में पूरी तरह से दृश्यमान हैं या नहीं, हम वर्टीकल समयरेखा के लिए उपयोग किए गए उसी कोड का लाभ उठाएंगे। इस Stack Overflow thread से आने वाला आवश्यक कोड निम्नानुसार है:
function isElementInViewport(el) { const rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }
उपर्युक्त फंक्शन से परे, हम दूसरे हेल्पर को डिफाइन करते हैं:
function setBtnState(el, flag = true) { if (flag) { el.classList.add(disabledClass); } else { if (el.classList.contains(disabledClass)) { el.classList.remove(disabledClass); } el.disabled = false; } }
यह फ़ंक्शन flag
पैरामीटर के मान के आधार पर एक एलिमेंट से disabled
क्लास को जोड़ता है या निकालता है। इसके अलावा, यह इस एलिमेंट के लिए डिसेबल्ड स्टेट को बदल सकता है।
हमने जो ऊपर वर्णित किया है, यह देखते हुए कि हम एनीमेशन को रोकना चाहिए या नहीं, यह जांचने के लिए कोड है:
for (let i = 0; i < el.length; i++) { el[i].addEventListener("click", function() { // other code here // code for stopping the animation setTimeout(() => { isElementInViewport(firstItem) ? setBtnState(arrowPrev) : setBtnState(arrowPrev, false); isElementInViewport(lastItem) ? setBtnState(arrowNext) : setBtnState(arrowNext, false); }, 1100); // other code here }); }
ध्यान दें कि इस कोड को एक्सेक्यूट करने से पहले 1.1 सेकंड की देरी है। ऐसा क्यों होता है?
अगर हम अपने CSS पर वापस जाते हैं, तो हम यह नियम देखेंगे:
.timeline ol { transition: all 1s; }
इसलिए, समयरेखा एनीमेशन को पूरा करने के लिए 1 सेकंड की आवश्यकता है। जब तक यह पूरा हो जाता है, तब तक हम 100 मिलीसेकेंड की प्रतीक्षा करते हैं और फिर, हम हमारा चेक परफॉर्म करते हैं।
यहां एनिमेशन के साथ समयरेखा है:
7. स्वाइप (Swipe) सपोर्ट जोड़ना
अब तक, टच इवेंट्स को टाइमलाइन रिस्पांस नहीं देता। अगर हम इस फंक्शनलिटी को जोड़ सकते हैं तो यह अच्छा होगा। इसे पूरा करने के लिए, हम अपना जावास्क्रिप्ट इम्प्लीमेंटेशन लिख सकते हैं या किसी संबंधित लाइब्रेरीज (जैसे कि Hammer.js, TouchSwipe.js) का उपयोग कर सकते हैं जो वहां मौजूद हैं।
हमारे डेमो के लिए, हम यह सरल रखेंगे और Hammer.js का उपयोग करेंगे, पहले तो, हम इस लाइब्रेरी को हमारे पेन में शामिल करते हैं:



फिर हम संबंधित फ़ंक्शन को डिक्लेअर करते हैं:
function setSwipeFn(tl, prev, next) { const hammer = new Hammer(tl); hammer.on("swipeleft", () => next.click()); hammer.on("swiperight", () => prev.click()); }
फंक्शन के अंदर, हम निम्नलिखित कार्य करते हैं:
- Hammer का एक इंस्टैंस बनाएं
-
swipeleft
औरswiperight
इवेंट्स के लिए हैंडलर्स रजिस्टर करें। - जब हम बाईं ओर की समयरेखा पर स्वाइप करते हैं, तो हम next बटन पर एक क्लिक ट्रिगर करते हैं, और इस प्रकार समयरेखा को दाईं ओर से एनिमेट किया जाता है।
- जब हम दांयीं दिशा में समयरेखा पर स्वाइप करते हैं, तो हम previous बटन पर एक ट्रिगर क्लिक करते हैं, और इस प्रकार समयरेखा बाईं ओर से एनिमेटेड है।
स्वाइप सपोर्ट के साथ समयरेखा:
कीबोर्ड नेविगेशन जोड़ना
कीबोर्ड नेविगेशन के लिए सपोर्ट प्रदान करके यूज़र्स के अनुभव को और बढ़ाएं। हमारे लक्ष्य:
- जब left or right arrow key दबाई जाती है, तो डॉक्यूमेंट को समयरेखा के शीर्ष स्थान पर स्क्रॉल किया जाना चाहिए (यदि कोई अन्य पेज सेक्शन वर्तमान में दिखाई देता है)। यह सुनिश्चित करता है कि पूरी टाइमलाइन दिखाई देगी।
- विशेष रूप से, जब left arrow key दबाई जाती है, तो समयरेखा बाएं से दाएं एनिमेट की जानी चाहिए।
- उसी तरह, जब right arrow key दबाई जाती है, तो समयरेखा को दांयें से बाएं ओर एनिमेट किया जाना चाहिए।
संबंधित फ़ंक्शन निम्न है:
function setKeyboardFn(prev, next) { document.addEventListener("keydown", (e) => { if ((e.which === 37) || (e.which === 39)) { const timelineOfTop = timeline.offsetTop; const y = window.pageYOffset; if (timelineOfTop !== y) { window.scrollTo(0, timelineOfTop); } if (e.which === 37) { prev.click(); } else if (e.which === 39) { next.click(); } } }); }
कीबोर्ड सपोर्ट के साथ समयरेखा:
8. रेस्पॉन्सिव के लिए जाना
हम लगभग पूरा कर चुके हैं! अंतिम लेकिन किसी से काम नहीं, हम समयरेखा रेस्पॉन्सिव बनाते हैं। जब व्यूपोर्ट 600 px से कम है, तो इसमें निम्नलिखित स्टैक्ड लेआउट होना चाहिए:



जैसा कि हम एक डेस्कटॉप-फर्स्ट एप्रोच का उपयोग कर रहे हैं, यहां CSS नियम हैं जो हमें ओवरराइट करना है:
@media screen and (max-width: 599px) { .timeline ol, .timeline ol li { width: auto; } .timeline ol { padding: 0; transform: none !important; } .timeline ol li { display: block; height: auto; background: transparent; } .timeline ol li:first-child { margin-top: 25px; } .timeline ol li:not(:first-child) { margin-left: auto; } .timeline ol li div { width: 94%; height: auto !important; margin: 0 auto 25px; } .timeline ol li:nth-child div { position: static; } .timeline ol li:nth-child(odd) div { transform: none; } .timeline ol li:nth-child(odd) div::before, .timeline ol li:nth-child(even) div::before { left: 50%; top: 100%; transform: translateX(-50%); border: none; border-left: 1px solid white; height: 25px; } .timeline ol li:last-child, .timeline ol li:nth-last-child(2) div::before, .timeline ol li:not(:last-child)::after, .timeline .arrows { display: none; } }
नोट: ऊपर दिए गए दो नियमों के लिए, हमें जावास्क्रिप्ट के द्वारा लागू की गई इनलाइन स्टाइल्स को ओवरराइड करने के लिए !important
नियम का उपयोग करना पड़ा।
हमारी टाइमलाइन की अंतिम स्थिति:
ब्राउज़र सपोर्ट
डेमो सभी हाल के ब्राउज़रों और डिवाइस में अच्छी तरह से काम करता है। इसके अलावा, जैसा कि आपने संभवतः देखा है, हम अपने ES6 कोड को ES5 में संकलित करने के लिए Babel का उपयोग करते हैं।
यह परीक्षण करने के दौरान एकलौती छोटी समस्या का मुझे सामना करना पड़ा वो है टेक्स्ट रेंडरिंग परिवर्तन जो तब होता है जब समयरेखा एनिमेटेड हो जाती है। हालांकि मैंने विभिन्न स्टैक ओवरफ़्लो थ्रेड्स में प्रस्तावित विभिन्न तरीकों की कोशिश की, मुझे सभी ऑपरेटिंग सिस्टम और ब्राउज़र के लिए सीधा समाधान नहीं मिला। इसलिए, अपने दिमाग में रखें कि समयरेखा एनिमेटेड होने के कारण आपको छोटे फ़ॉन्ट रेंडरिंग समस्याएं मिल सकती हैं।
निष्कर्ष
इस पर्याप्त रूप से पर्याप्त ट्यूटोरियल में, हमने एक साधारण आर्डर लिस्ट से शुरुआत की और एक रेस्पॉन्सिव हॉरिजॉन्टल समयरेखा तैयार की। शक के बिना, हमने बहुत सी दिलचस्प चीजों को शामिल किया है, लेकिन मुझे आशा है कि आपको अंतिम परिणाम की दिशा में काम करने का मज़ा आया और यह आपको कुछ नए ज्ञान प्राप्त करने में मदद करता है।
यदि आपके पास कोई सवाल है या यदि आपको कुछ भी समझ नहीं आया है, तो मुझे नीचे दिए गए कमैंट्स में बताएं!
अगला कदम
यदि आप इस समयरेखा को और सुधार या विस्तारित करना चाहते हैं, तो यहां कुछ चीजें हैं जो आप कर सकते हैं:
- खींचने (dragging) के लिए सपोर्ट जोड़ें। नेविगेट करने के लिए टाइमलाइन बटन पर क्लिक करने के बजाय, हम केवल समयरेखा क्षेत्र को ड्रैग कर सकते हैं। इस व्यवहार के लिए, आप नेटिव Drag and Drop Api (जो दुर्भाग्य से लेखन के समय मोबाइल उपकरणों का सपोर्ट नहीं करते हैं) या Draggable.js जैसे बाहरी library का उपयोग कर सकते हैं।
- समयरेखा बिहेवियर में सुधार करें जैसे ही हम ब्राउज़र विंडो का आकार बदलते हैं। उदाहरण के लिए, जैसे हम विंडो का आकार बदलते हैं, बटन को इनेबल और डिसएबल किया जाना चाहिए।
- कोड को अधिक प्रबंधनीय तरीके से व्यवस्थित करें शायद, एक सामान्य जावास्क्रिप्ट डिजाइन पैटर्न का उपयोग करें।