CSS और JavaScript के साथ एक स्थानांतरण अंडरलाइन होवर प्रभाव (Shifting Underline Hover Effect) कैसे बनाएं.
Hindi (हिंदी) translation by Ashish Rampal (you can also view the original English article)
आज के टुटोरिअल में, हम फैंसी मेनू होवर इफ़ेक्ट (fancy menu hover effect) बनाने के लिए कुछ CSS और JavaScript का प्रयोग करेंगे. यह कोई पेचीदा अंतिम परिणाम नहीं है, फिर भी हमारे front-end स्किल की प्रैक्टिस के लिए इसको बनाना बहुत बड़ा अवसर होगा.
आगे के परिचय के बिना, आईये हम क्या बनाएंगे इसकी जांच कर लें.
The Markup
हम कुछ बेहद ही बुनयादी मार्कअप से शुरू करेंगे: एक nav
एलिमेंट जिसमे मेनू और एक खाली span
एलिमेंट शामिल हैं.
<nav class="mynav"> <ul> <li> <a href="">Home</a> </li> <li> <a href="">About</a> </li> <li> <a href="">Company</a> </li> <li> <a href="">Work</a> </li> <li> <a href="">Clients</a> </li> <li> <a href="">Contact</a> </li> </ul> </nav> <span class="target"></span>
The CSS
मार्कअप के तैयार होने के बाद, हम सम्बंधित एलिमेंट्स के लिए कुछ बुनयादी स्टाइल्स को specify करेंगे.
.mynav ul { display: flex; justify-content: center; flex-wrap: wrap; list-style-type: none; padding: 0; } .mynav li:not(:last-child) { margin-right: 20px; } .mynav a { display: block; font-size: 20px; color: black; text-decoration: none; padding: 7px 15px; } .target { position: absolute; border-bottom: 4px solid transparent; z-index: -1; transform: translateX(-60px); } .mynav a, .target { transition: all .35s ease-in-out; }
ध्यान दीजिये की span
एलिमेंट (.target
) की position absolute है. जैसा की हम कुछ क्षण के बाद देखेंगे, हम JavaScript का प्रयोग इसकी सटीक स्थिति पता करने के लिए करेंगे. इसके साथ-साथ, यह मेनू के लिंक के पीछे दिखना चाहिए, इसलिए हमने इसे नकारात्मक z-index
दिया.
The JavaScript
इस समय, आईये हम अपना ध्यान आवश्यक JavaScript पर लगते हैं. शुरुआत के लिए, हम वांछित (desired) एलिमेंट्स को लक्ष्य बनाते हैं. हम रंगों का एक array भी बनाते हैं जिसका प्रयोग हम बाद में करेंगे.
const target = document.querySelector(".target"); const links = document.querySelectorAll(".mynav a"); const colors = ["deepskyblue", "orange", "firebrick", "gold", "magenta", "black", "darkblue"];
घटनाएं (Events)
इसके बाद हम मेनू के लिंक्स के click
और mouseenter
इवेंट्स को सुनते हैं.
जब click
इवेंट घटित होता है, हम पेज को फिर से लोड होने से रोकते हैं. बेशक यह हमारे मामले में काम करेगा क्योंकि सभी लिंक्स में खाली href
ऐट्रिब्यूट्स हैं. हांलांकि असली प्रोजेक्ट में, हर एक मेनू लिंक अलग अलग पेज खोलेगा.
सबसे महत्वपुर्ण बात, जैसे ही mouseenter
इवेंट चालू (fire) होता है, mouseenterFunc
नाम का callback फंक्शन कार्यान्वित (execute) हो जाता है.
for (let i = 0; i < links.length; i++) { links[i].addEventListener("click", (e) => e.preventDefault()); links[i].addEventListener("mouseenter", mouseenterFunc); }
mouseenterFunc
mouseenterFunc
फंक्शन की बॉडी इस तरह की होगी:
function mouseenterFunc() { for (let i = 0; i < links.length; i++) { if (links[i].parentNode.classList.contains("active")) { links[i].parentNode.classList.remove("active"); } links[i].style.opacity = "0.25"; } this.parentNode.classList.add("active"); this.style.opacity = "1"; const width = this.getBoundingClientRect().width; const height = this.getBoundingClientRect().height; const left = this.getBoundingClientRect().left; const top = this.getBoundingClientRect().top; const color = colors[Math.floor(Math.random() * colors.length)]; target.style.width = `${width}px`; target.style.height = `${height}px`; target.style.left = `${left}px`; target.style.top = `${top}px`; target.style.borderColor = color; target.style.transform = "none"; }
इस फंक्शन के अंदर हम निम्नलिखित कार्ये करेंगे:
- टारगेट लिंक के सबसे निकटतम पैरेंट (
li
) मेंactive
क्लास जोड़ेंगे. - सभी मेनू लिंक्स से
opacity
को घटाएंगे, "active" को छोड़ के. - साथ वाले लिंक का पता लगाने और इसकी पोजीशन को viewport के रिलेटिव बनाने के लिए
getBoundingClientRect
method का प्रयोग करेंगे. - उपरोक्त array में से रैंडम रंग (color) को प्राप्त करेंगे और इसे
span
एलिमेंट कीborder-color
प्रॉपर्टी की वैल्यू के तौर पर पास (pass) करेंगे. याद रखें, इसकी शुरुआती प्रॉपर्टी वैल्यूtransparent
सेट है. -
getBoundingClientRect
method से निकली गयी वैल्यू कोspan
एलिमेंट की सम्बंधित प्रॉपर्टी में दे देंगे. दूसरे शब्दो में,span
tag उस लिंक के साइज और पोजीशन को inherit करेगा जिसके ऊपर hover किया जा रहा है. -
span
एलिमेंट में दी गयी डिफ़ॉल्ट ट्रांसफॉर्मेशन (default transformation) को रिसेट (reset) करें. यह बिहेवियर (behavior) सिर्फ पहली बार लिंक पर होवर करने पर ही जरूरी है. इस स्थिति में, एलिमेंट का ट्रांसफॉर्मेशनtransform: translateX(-60px)
सेtransform: none
हो जायेगा. यह हमें एक प्यारा सा slide-in इफ़ेक्ट देगा.
यदि Active हो
यह नोट करना महत्वपुर्ण है की ऊपर दिया गया कोड हर बार लिंक के ऊपर होवर करने पर एक्सेक्युट (execute) किया जाता है. इसलिए यह "active" लिंक के ऊपर होवर करने पर भी चलता है. इस बिहेवियर को रोकने के लिए, हम ऊपर दिए गए कोड को if
statement के अंदर डालेंगे.
function mouseenterFunc() { if (!this.parentNode.classList.contains("active")) { // code here } }
अब तक हमारा demo कुछ इस तरह दिखेगा:
लगभग, पर संपुर्ण तरीके से नहीं
तो, सब कुछ वैसा ही हो रहा है जैसा सोचा था, है न? पर यह सच नहीं है क्योंकि अगर हम पेज में स्क्रॉल (scroll) करेंगे, या viewport के साइज में परिवर्तन करेंगे, और उसके बाद लिंक (link) को सेलेक्ट करने की कोशिश करेंगे, तो चीज़े अव्यवस्थित हो जाएंगी. खास तौर पर span
एलिमेंट की पोजीशन गलत हो जाएंगी.
मेरी बात समझने के लिए पुरे पेज के डेमो (demo) पर कार्य करो.
इसका हल करने के लिए, हमे यह हिसाब लगाना पड़ेगा की window के टॉप से हमने कितना स्क्रॉल (scroll) कर लिया है और इस वैल्यू को टारगेट एलिमेंट की वर्तमान top
वैल्यू में जोड़ देंगे. इसी प्रकार से, हमें यह भी पता लगाना होगा की document को horizontally (इस मामले में) कितना स्क्रॉल किया जा चूका है. नतीजे से निकली वैल्यू को टारगेट एलिमेंट की वर्तमान left
वैल्यू में जोड़ देंगे.
यह कोड की दो लाइन्स है जिन्हें हमने अपडेट (update) किया है.
const left = this.getBoundingClientRect().left + window.pageXOffset; const top = this.getBoundingClientRect().top + window.pageYOffset;
ध्यान में रखें की ऊपर दिए गए सभी कोड एक्सेक्युट (execute) हो जाएंगे जैसे ही ब्राउज़र DOM बनाने की प्रक्रिया करता है और उसे उचित स्क्रिप्ट (script) मिल जाती है. फिर से, आप की खुद के कार्यान्वित और डिजाईन में आप इस कोड को पेज लोड होते वक्त चलाना चाहें या इसी तरह कुछ. इस परिदृष्य (scenario) में, आपको इसे इवेंट हैंडलर (event handler) में जोड़ना होगा (उदाहरण के लिए load
इवेंट).
Viewport
अंतिम चीज, हमे यह पक्का करना पड़ेगा की ब्राउज़र विंडो के रीसाइज (resize) करने पर भी इफ़ेक्ट चलते रहे. इसे पूरा करने के लिए, हमे resize
इवेंट को सुनते हैं और resizeFunc
नाम के इवेंट हैंडलर (event handler) को रजिस्टर करते हैं.
window.addEventListener("resize", resizeFunc);
यह हैंडलर की बॉडी है:
function resizeFunc() { const active = document.querySelector(".mynav li.active"); if (active) { const left = active.getBoundingClientRect().left + window.pageXOffset; const top = active.getBoundingClientRect().top + window.pageYOffset; target.style.left = `${left}px`; target.style.top = `${top}px`; } }
ऊपर दिए गए फंक्शन में, हम निम्नलिखित चीजे करेंगे:
- जाँच कीजिये यदि menu लिस्ट आइटम में कोई
active
क्लास है की नहीं. यदि कोई एलिमेंट ऐसा है, जो बताता है की हम पहले से ही किसी लिंक पर hovered है. - "active" आइटम की
left
औरtop
प्रॉपर्टीज और साथ ही साथ उससे सम्बंधित विंडो प्रॉपर्टीज को लें और उसेspan
एलिमेंट में असाइन कर दें. ध्यान दें की हम सिर्फ उन प्रॉपर्टीज की वैल्यूज को प्राप्त करेंगे जोresize
इवेंट के दौरान परिवर्तित हो जाती है. इसका अर्थ है, menu links की width और height की फिर से गणना करने की जरूरत नहीं है.
Browser का आधार (Support)
यह डेमो सभी नए ब्राउर्स के साथ अच्छे से कार्य करेगा. हांलांकि यदि आप को कोई मुश्किल का सामना करना पड़ता है, नीचे comments में मुझे बताएं. साथ ही साथ, जैसा की आप ने ध्यान दिया होगा, हमने ES6 कोड को निचले ES5 में compile करने के लिए Babel का प्रयोग किया.
निष्कर्ष
इस फुर्ती से दी गए सलाह में हम सरल फिर भी दिलचस्प मेनू होवर (menu hover) के इफ़ेक्ट को बनाने की प्रक्रिया से गुजरे.
मुझे उम्मीद है की जो हमने बनाया उसमे आपको मजा आया होगा और आपको और अधिक शक्तिशाली मेनू इफेक्ट्स (menu effect) बनाने जैसा की एक Stripe साइट में (लिखते समय) दिख रहा है के लिए प्रेरणा मिलेगी.
क्या आपने ऐसा ही कुछ कभी बनाया है? अगर हाँ, आपने क्या क्या चुनोतियों का सामना किया यह हमसे साँझा करना सुनिश्चित करें.