# टेरारियम प्रोजेक्ट भाग 3: DOM मैनिपुलेशन और एक क्लोजर ![DOM और एक क्लोजर](../../../../sketchnotes/webdev101-js.png) > स्केच नोट [टोमोमी इमुरा](https://twitter.com/girlie_mac) द्वारा ## प्री-लेक्चर क्विज़ [प्री-लेक्चर क्विज़](https://ff-quizzes.netlify.app/web/quiz/19) ### परिचय DOM, या "डॉक्यूमेंट ऑब्जेक्ट मॉडल", को मैनिपुलेट करना वेब डेवलपमेंट का एक महत्वपूर्ण पहलू है। [MDN](https://developer.mozilla.org/docs/Web/API/Document_Object_Model/Introduction) के अनुसार, "डॉक्यूमेंट ऑब्जेक्ट मॉडल (DOM) वेब पर किसी डॉक्यूमेंट की संरचना और सामग्री को बनाने वाले ऑब्जेक्ट्स का डेटा प्रतिनिधित्व है।" वेब पर DOM मैनिपुलेशन से जुड़ी चुनौतियां अक्सर जावास्क्रिप्ट फ्रेमवर्क का उपयोग करने का कारण रही हैं, लेकिन हम इसे वैनिला जावास्क्रिप्ट के साथ प्रबंधित करेंगे! इसके अलावा, इस पाठ में [जावास्क्रिप्ट क्लोजर](https://developer.mozilla.org/docs/Web/JavaScript/Closures) का विचार पेश किया जाएगा, जिसे आप एक ऐसी फ़ंक्शन के रूप में सोच सकते हैं जो किसी अन्य फ़ंक्शन के अंदर बंद है, जिससे आंतरिक फ़ंक्शन को बाहरी फ़ंक्शन के स्कोप तक पहुंच मिलती है। > जावास्क्रिप्ट क्लोजर एक व्यापक और जटिल विषय है। यह पाठ केवल इस बुनियादी विचार को छूता है कि इस टेरारियम के कोड में, आपको एक क्लोजर मिलेगा: एक आंतरिक फ़ंक्शन और एक बाहरी फ़ंक्शन इस तरह से निर्मित होते हैं कि आंतरिक फ़ंक्शन को बाहरी फ़ंक्शन के स्कोप तक पहुंच मिलती है। इस पर अधिक जानकारी के लिए [विस्तृत दस्तावेज़](https://developer.mozilla.org/docs/Web/JavaScript/Closures) देखें। हम DOM को मैनिपुलेट करने के लिए एक क्लोजर का उपयोग करेंगे। DOM को एक पेड़ के रूप में सोचें, जो यह दर्शाता है कि वेब पेज डॉक्यूमेंट को कितने तरीकों से मैनिपुलेट किया जा सकता है। विभिन्न APIs (एप्लिकेशन प्रोग्राम इंटरफेस) लिखे गए हैं ताकि प्रोग्रामर अपनी पसंद की प्रोग्रामिंग भाषा का उपयोग करके DOM तक पहुंच सकें और इसे संपादित, बदल, पुनः व्यवस्थित और अन्य तरीकों से प्रबंधित कर सकें। ![DOM ट्री का प्रतिनिधित्व](../../../../3-terrarium/3-intro-to-DOM-and-closures/images/dom-tree.png) > DOM और उससे जुड़े HTML मार्कअप का एक प्रतिनिधित्व। [ओल्फा नस्रौई](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites) से इस पाठ में, हम अपने इंटरैक्टिव टेरारियम प्रोजेक्ट को पूरा करेंगे, जिसमें जावास्क्रिप्ट का उपयोग करके उपयोगकर्ता को पेज पर पौधों को मैनिपुलेट करने की अनुमति दी जाएगी। ### पूर्वापेक्षा आपके टेरारियम के लिए HTML और CSS तैयार होना चाहिए। इस पाठ के अंत तक, आप पौधों को टेरारियम में खींचने और बाहर निकालने में सक्षम होंगे। ### कार्य अपने टेरारियम फ़ोल्डर में एक नई फ़ाइल बनाएं जिसका नाम `script.js` हो। उस फ़ाइल को `` सेक्शन में इंपोर्ट करें: ```html ``` > नोट: HTML फ़ाइल को पूरी तरह से लोड होने के बाद ही जावास्क्रिप्ट को निष्पादित करने की अनुमति देने के लिए `defer` का उपयोग करें। आप `async` एट्रिब्यूट का भी उपयोग कर सकते हैं, जो स्क्रिप्ट को HTML फ़ाइल के पार्सिंग के दौरान निष्पादित करने की अनुमति देता है, लेकिन हमारे मामले में, यह महत्वपूर्ण है कि ड्रैग स्क्रिप्ट को निष्पादित करने से पहले HTML तत्व पूरी तरह से उपलब्ध हों। --- ## DOM तत्व सबसे पहले आपको उन तत्वों के संदर्भ बनाने की आवश्यकता है जिन्हें आप DOM में मैनिपुलेट करना चाहते हैं। हमारे मामले में, ये 14 पौधे हैं जो वर्तमान में साइड बार में प्रतीक्षा कर रहे हैं। ### कार्य ```html dragElement(document.getElementById('plant1')); dragElement(document.getElementById('plant2')); dragElement(document.getElementById('plant3')); dragElement(document.getElementById('plant4')); dragElement(document.getElementById('plant5')); dragElement(document.getElementById('plant6')); dragElement(document.getElementById('plant7')); dragElement(document.getElementById('plant8')); dragElement(document.getElementById('plant9')); dragElement(document.getElementById('plant10')); dragElement(document.getElementById('plant11')); dragElement(document.getElementById('plant12')); dragElement(document.getElementById('plant13')); dragElement(document.getElementById('plant14')); ``` यहां क्या हो रहा है? आप डॉक्यूमेंट का संदर्भ ले रहे हैं और उसके DOM में एक विशेष Id वाले तत्व को खोज रहे हैं। याद रखें कि HTML पर पहले पाठ में आपने प्रत्येक पौधे की छवि को व्यक्तिगत Ids (`id="plant1"`) दिए थे? अब आप उस प्रयास का उपयोग करेंगे। प्रत्येक तत्व की पहचान करने के बाद, आप उस आइटम को `dragElement` नामक एक फ़ंक्शन में पास करते हैं जिसे आप अभी बनाने वाले हैं। इस प्रकार, HTML में तत्व अब ड्रैग-इनेबल्ड हो गया है, या जल्द ही हो जाएगा। ✅ हम तत्वों को Id से क्यों संदर्भित करते हैं? CSS क्लास से क्यों नहीं? इस प्रश्न का उत्तर देने के लिए आप CSS पर पिछले पाठ का संदर्भ ले सकते हैं। --- ## क्लोजर अब आप `dragElement` क्लोजर बनाने के लिए तैयार हैं, जो एक बाहरी फ़ंक्शन है जो एक या अधिक आंतरिक फ़ंक्शनों को संलग्न करता है (हमारे मामले में, तीन होंगे)। क्लोजर तब उपयोगी होते हैं जब एक या अधिक फ़ंक्शनों को बाहरी फ़ंक्शन के स्कोप तक पहुंच की आवश्यकता होती है। यहां एक उदाहरण है: ```javascript function displayCandy(){ let candy = ['jellybeans']; function addCandy(candyType) { candy.push(candyType) } addCandy('gumdrops'); } displayCandy(); console.log(candy) ``` इस उदाहरण में, `displayCandy` फ़ंक्शन एक फ़ंक्शन को घेरता है जो पहले से मौजूद एक ऐरे में एक नई कैंडी प्रकार को पुश करता है। यदि आप इस कोड को चलाते हैं, तो `candy` ऐरे अनिर्धारित होगा, क्योंकि यह एक स्थानीय वेरिएबल है (क्लोजर के लिए स्थानीय)। ✅ आप `candy` ऐरे को कैसे सुलभ बना सकते हैं? इसे क्लोजर के बाहर ले जाकर देखें। इस तरह, ऐरे ग्लोबल हो जाएगा, बजाय इसके कि यह केवल क्लोजर के स्थानीय स्कोप में उपलब्ध हो। ### कार्य `script.js` में तत्व घोषणाओं के नीचे एक फ़ंक्शन बनाएं: ```javascript function dragElement(terrariumElement) { //set 4 positions for positioning on the screen let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; terrariumElement.onpointerdown = pointerDrag; } ``` `dragElement` को इसका `terrariumElement` ऑब्जेक्ट स्क्रिप्ट के शीर्ष पर घोषणाओं से मिलता है। फिर, आप उस फ़ंक्शन में पास किए गए ऑब्जेक्ट के लिए कुछ स्थानीय पोजीशन को `0` पर सेट करते हैं। ये स्थानीय वेरिएबल्स हैं जिन्हें आप प्रत्येक तत्व के लिए मैनिपुलेट करेंगे क्योंकि आप क्लोजर के भीतर ड्रैग और ड्रॉप कार्यक्षमता जोड़ते हैं। टेरारियम इन खींचे गए तत्वों से भरा जाएगा, इसलिए एप्लिकेशन को यह ट्रैक रखना होगा कि उन्हें कहां रखा गया है। इसके अलावा, `terrariumElement` जिसे इस फ़ंक्शन में पास किया गया है, को एक `pointerdown` इवेंट सौंपा गया है, जो [वेब APIs](https://developer.mozilla.org/docs/Web/API) का हिस्सा है जो DOM प्रबंधन में मदद करने के लिए डिज़ाइन किया गया है। `onpointerdown` तब फायर होता है जब कोई बटन दबाया जाता है, या हमारे मामले में, एक ड्रैग करने योग्य तत्व को छुआ जाता है। यह इवेंट हैंडलर [वेब और मोबाइल ब्राउज़रों](https://caniuse.com/?search=onpointerdown) दोनों पर काम करता है, कुछ अपवादों के साथ। ✅ [इवेंट हैंडलर `onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) में क्रॉस-ब्राउज़र अधिक समर्थन है; आप इसे यहां क्यों नहीं उपयोग करेंगे? उस सटीक प्रकार की स्क्रीन इंटरैक्शन के बारे में सोचें जिसे आप यहां बनाना चाहते हैं। --- ## पॉइंटरड्रैग फ़ंक्शन `terrariumElement` को इधर-उधर खींचने के लिए तैयार है; जब `onpointerdown` इवेंट फायर होता है, तो `pointerDrag` फ़ंक्शन को बुलाया जाता है। इस लाइन के ठीक नीचे वह फ़ंक्शन जोड़ें: `terrariumElement.onpointerdown = pointerDrag;`: ### कार्य ```javascript function pointerDrag(e) { e.preventDefault(); console.log(e); pos3 = e.clientX; pos4 = e.clientY; } ``` कई चीजें होती हैं। सबसे पहले, आप `e.preventDefault();` का उपयोग करके सामान्य घटनाओं को रोकते हैं जो आमतौर पर `pointerdown` पर होती हैं। इस तरह आपके पास इंटरफ़ेस के व्यवहार पर अधिक नियंत्रण होता है। > जब आपने स्क्रिप्ट फ़ाइल को पूरी तरह से बना लिया हो, तो इस लाइन पर वापस आएं और इसे बिना `e.preventDefault()` के आज़माएं - क्या होता है? दूसरा, `index.html` को एक ब्राउज़र विंडो में खोलें और इंटरफ़ेस का निरीक्षण करें। जब आप किसी पौधे पर क्लिक करते हैं, तो आप देख सकते हैं कि 'e' इवेंट कैसे कैप्चर किया गया है। इवेंट में कितनी जानकारी एकत्र की जाती है, यह देखने के लिए इवेंट को गहराई से देखें! इसके बाद, ध्यान दें कि स्थानीय वेरिएबल्स `pos3` और `pos4` को `e.clientX` पर कैसे सेट किया गया है। आप निरीक्षण पैन में `e` मान पा सकते हैं। ये मान उस क्षण पौधे के x और y निर्देशांक को कैप्चर करते हैं जब आप उस पर क्लिक या टच करते हैं। आपको पौधों के व्यवहार पर बारीकी से नियंत्रण की आवश्यकता होगी क्योंकि आप उन्हें क्लिक और ड्रैग करते हैं, इसलिए आप उनके निर्देशांक को ट्रैक करते हैं। ✅ क्या यह अधिक स्पष्ट हो रहा है कि यह पूरा ऐप एक बड़े क्लोजर के साथ क्यों बनाया गया है? यदि ऐसा नहीं होता, तो आप 14 ड्रैग करने योग्य पौधों में से प्रत्येक के लिए स्कोप को कैसे बनाए रखते? प्रारंभिक फ़ंक्शन को पूरा करने के लिए `pos4 = e.clientY` के नीचे दो और पॉइंटर इवेंट मैनिपुलेशन जोड़ें: ```html document.onpointermove = elementDrag; document.onpointerup = stopElementDrag; ``` अब आप यह संकेत दे रहे हैं कि आप पौधे को पॉइंटर के साथ खींचना चाहते हैं जैसे ही आप इसे हिलाते हैं, और खींचने की प्रक्रिया को रोकना चाहते हैं जब आप पौधे को अनचयनित करते हैं। `onpointermove` और `onpointerup` उसी API का हिस्सा हैं जैसे `onpointerdown`। इंटरफ़ेस अब त्रुटियां फेंकेगा क्योंकि आपने अभी तक `elementDrag` और `stopElementDrag` फ़ंक्शंस को परिभाषित नहीं किया है, इसलिए उन्हें अगला बनाएं। ## `elementDrag` और `stopElementDrag` फ़ंक्शंस आप दो और आंतरिक फ़ंक्शंस जोड़कर अपने क्लोजर को पूरा करेंगे जो यह संभालेंगे कि जब आप पौधे को खींचते हैं और खींचना बंद करते हैं तो क्या होता है। आप जो व्यवहार चाहते हैं वह यह है कि आप किसी भी समय किसी भी पौधे को खींच सकें और उसे स्क्रीन पर कहीं भी रख सकें। यह इंटरफ़ेस काफी अनुकूल है (उदाहरण के लिए, कोई ड्रॉप ज़ोन नहीं है) ताकि आप अपने टेरारियम को अपनी पसंद के अनुसार डिज़ाइन कर सकें, पौधों को जोड़कर, हटाकर और पुनः स्थिति देकर। ### कार्य `pointerDrag` के समापन कर्ली ब्रैकेट के ठीक बाद `elementDrag` फ़ंक्शन जोड़ें: ```javascript function elementDrag(e) { pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; console.log(pos1, pos2, pos3, pos4); terrariumElement.style.top = terrariumElement.offsetTop - pos2 + 'px'; terrariumElement.style.left = terrariumElement.offsetLeft - pos1 + 'px'; } ``` इस फ़ंक्शन में, आप उन प्रारंभिक पोजीशन्स 1-4 को बहुत अधिक संपादित करते हैं जिन्हें आपने बाहरी फ़ंक्शन में स्थानीय वेरिएबल्स के रूप में सेट किया था। यहां क्या हो रहा है? जैसे ही आप खींचते हैं, आप `pos1` को फिर से असाइन करते हैं, इसे `pos3` (जिसे आपने पहले `e.clientX` के रूप में सेट किया था) माइनस वर्तमान `e.clientX` मान के बराबर बनाते हैं। आप `pos2` के साथ एक समान ऑपरेशन करते हैं। फिर, आप `pos3` और `pos4` को तत्व के नए X और Y निर्देशांक पर रीसेट करते हैं। आप इन परिवर्तनों को कंसोल में खींचते समय देख सकते हैं। फिर, आप पौधे की css शैली को मैनिपुलेट करते हैं ताकि इसे नई पोजीशन पर सेट किया जा सके, जो `pos1` और `pos2` की नई पोजीशन के आधार पर पौधे के शीर्ष और बाएं X और Y निर्देशांक की गणना करता है। > `offsetTop` और `offsetLeft` CSS प्रॉपर्टीज़ हैं जो किसी तत्व की स्थिति को उसके पैरेंट के आधार पर सेट करती हैं; इसका पैरेंट कोई भी तत्व हो सकता है जो `static` के रूप में पोजिशन नहीं किया गया हो। यह सभी पोजीशनिंग की पुनर्गणना टेरारियम और उसके पौधों के व्यवहार को बारीकी से ट्यून करने की अनुमति देती है। ### कार्य इंटरफ़ेस को पूरा करने का अंतिम कार्य `stopElementDrag` फ़ंक्शन को `elementDrag` के समापन कर्ली ब्रैकेट के बाद जोड़ना है: ```javascript function stopElementDrag() { document.onpointerup = null; document.onpointermove = null; } ``` यह छोटा फ़ंक्शन `onpointerup` और `onpointermove` इवेंट्स को रीसेट करता है ताकि आप या तो अपने पौधे की प्रगति को फिर से शुरू कर सकें या एक नया पौधा खींचना शुरू कर सकें। ✅ क्या होता है यदि आप इन इवेंट्स को null पर सेट नहीं करते? अब आपने अपना प्रोजेक्ट पूरा कर लिया है! 🥇बधाई हो! आपने अपना सुंदर टेरारियम पूरा कर लिया है। ![समाप्त टेरारियम](../../../../3-terrarium/3-intro-to-DOM-and-closures/images/terrarium-final.png) --- ## 🚀चुनौती अपने क्लोजर में एक नया इवेंट हैंडलर जोड़ें ताकि पौधों के साथ कुछ और किया जा सके; उदाहरण के लिए, किसी पौधे को सामने लाने के लिए उस पर डबल-क्लिक करें। रचनात्मक बनें! ## पोस्ट-लेक्चर क्विज़ [पोस्ट-लेक्चर क्विज़](https://ff-quizzes.netlify.app/web/quiz/20) ## समीक्षा और स्व-अध्ययन स्क्रीन पर तत्वों को खींचना तुच्छ लग सकता है, लेकिन इसे करने के कई तरीके हैं और कई समस्याएं हो सकती हैं, यह इस बात पर निर्भर करता है कि आप किस प्रकार का प्रभाव चाहते हैं। वास्तव में, एक पूरा [ड्रैग और ड्रॉप API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API) है जिसे आप आज़मा सकते हैं। हमने इस मॉड्यूल में इसका उपयोग नहीं किया क्योंकि हम जो प्रभाव चाहते थे वह थोड़ा अलग था, लेकिन इसे अपने प्रोजेक्ट पर आज़माएं और देखें कि आप क्या हासिल कर सकते हैं। पॉइंटर इवेंट्स पर अधिक जानकारी [W3C डॉक्स](https://www.w3.org/TR/pointerevents1/) और [MDN वेब डॉक्स](https://developer.mozilla.org/docs/Web/API/Pointer_events) पर पाएं। हमेशा ब्राउज़र क्षमताओं की जांच [CanIUse.com](https://caniuse.com/) का उपयोग करके करें। ## असाइनमेंट [DOM के साथ थोड़ा और काम करें](assignment.md) **अस्वीकरण**: यह दस्तावेज़ AI अनुवाद सेवा [Co-op Translator](https://github.com/Azure/co-op-translator) का उपयोग करके अनुवादित किया गया है। जबकि हम सटीकता सुनिश्चित करने का प्रयास करते हैं, कृपया ध्यान दें कि स्वचालित अनुवाद में त्रुटियां या अशुद्धियां हो सकती हैं। मूल दस्तावेज़, जो इसकी मूल भाषा में है, को प्रामाणिक स्रोत माना जाना चाहिए। महत्वपूर्ण जानकारी के लिए, पेशेवर मानव अनुवाद की सिफारिश की जाती है। इस अनुवाद के उपयोग से उत्पन्न किसी भी गलतफहमी या गलत व्याख्या के लिए हम उत्तरदायी नहीं हैं।