ब्राउज़र में WYSIWYG HTML संपादक। भाग ३

लेख में डिज़ाइनरोड और सामग्री के गुण का उपयोग करने के अभ्यास का वर्णन किया गया है, साथ ही साथ एक साधारण संपादक संपादक बनाने के उदाहरण का उपयोग करके संबंधित एपीआई।
श्रृंखला में पहले लेख का अनुवाद डिजाइनमोड और सामग्री का उपयोग करने के सिद्धांत की खोज करना और संबंधित एपीआई: भाग 1भाग २



परिचय

लेख के पहले भाग में, मैंने डिज़ाइनमोड और सामग्री के गुणों का उपयोग करके एक ब्राउज़र संपादक बनाने के सिद्धांत की विस्तार से जांच की। ये DOM प्रॉपर्टीज़ HTML 5 में मानकीकृत हैं और अधिकांश ब्राउज़र में कमोबेश समर्थित हैं। लेख के दूसरे भाग में, मैं एक सरल क्रॉस-ब्राउज़र पाठ संपादक के निर्माण पर विचार करते हुए सिद्धांत से अभ्यास की ओर बढ़ूंगा।

आप नेटवर्क पर संपादक का तैयार संस्करण देख सकते हैं और उसका कोड डाउनलोड कर सकते हैं। लिस्टिंग केवल कोड के सबसे दिलचस्प हिस्सों को दिखाती है जिन्हें स्पष्टीकरण की आवश्यकता होती है, बाकी कोड को माना नहीं जाता है, क्योंकि यह उबाऊ है। कोड तीन फ़ाइलों में विभाजित है:

ढांचा

आधार के रूप में, हम एक IFrame के अंदर एक खाली पृष्ठ का उपयोग करेंगे:
<iframe id="editorFrame" src="blank.html"></iframe>
      
      



हम शरीर के अंदर तत्वों के बिना, एक बिल्कुल खाली पृष्ठ प्राप्त करने के लिए रिक्त का उपयोग कर सकते हैं, लेकिन मैंने अपना "खाली" पृष्ठ बनाना पसंद किया, क्योंकि इससे हम शरीर में एक खाली अनुच्छेद के साथ काम करना शुरू कर सकेंगे।
 <title></title> <body><p></p></body>
      
      



यह बेहतर है क्योंकि मोज़िला खाली पी में टाइप करना शुरू करता है, अन्य सभी ब्राउज़रों की तरह। यदि ऐसा नहीं किया जाता है, तो वह सीधे शरीर में पाठ दर्ज करती है। ContentEditable संपत्ति का उपयोग करते हुए, हम एक फ्रेम के बिना कर सकते हैं, लेकिन Firefox 2 contentEditable का समर्थन नहीं करता है, इसलिए iFrame का उपयोग करना बेहतर है। ( अनुवादक का ध्यान दें: FF2 है, इसे हल्के ढंग से रखना, प्रासंगिक नहीं है। इसलिए, मुझे लगता है कि किसी को भी iframe की आवश्यकता नहीं है। )



संपादन मोड सेट करें

जब पृष्ठ लोड होता है, तो हम संपादन मोड को सक्षम करते हैं, फ़ंक्शन का उपयोग करते हुए (Editor.js में स्थित)
 function createEditor() { var editFrame = document.getElementById("editorFrame"); editFrame.contentWindow.document.designMode="on"; } bindEvent(window, "load", createEditor);
      
      



bindEvent किसी फ़ंक्शन को किसी इवेंट में बाँधने के लिए ज़िम्मेदार है (use.js में परिभाषित)। JQuery जैसे फ्रेमवर्क में उपयुक्त कार्यक्षमता है जिसका आप उपयोग करने की संभावना रखते हैं। अगला चरण न्यूनतम स्वरूपण सुविधाओं के साथ एक नियंत्रण कक्ष बनाना है।



नियंत्रण कक्ष

आइए एक सरल नियंत्रण से शुरू करें: "बोल्ड" बटन, जो चयनित पाठ की शैली को बोल्ड में बदल देगा। बटन को दस्तावेज़ की स्थिति को भी प्रदर्शित करना चाहिए - यदि पाठ के अंदर प्रवेश बिंदु बोल्ड है, तो बटन को हाइलाइट किया जाना चाहिए। तर्क को दो ऑब्जेक्ट्स के बीच विभाजित किया गया है: कमांड ऑब्जेक्ट, जो दस्तावेज़ पर वास्तविक संचालन को एन्क्रिप्ट करता है और चयन राज्य के लिए अनुरोध करता है, और नियंत्रक ऑब्जेक्ट, जो घटनाओं की प्रक्रिया करता है। बटन की स्थिति को क्लिक और सिंक्रनाइज़ करता है। पृथक्करण आवश्यक है, क्योंकि विभिन्न टीमों को एक ही तर्क साझा करना चाहिए, जैसा कि हम बाद में देखेंगे। घटनाओं को दो बिंदुओं पर ट्रिगर किया जाता है - जब उपयोगकर्ता नियंत्रण कक्ष पर एक बटन दबाता है, तो नियंत्रक एक आदेश को कॉल करता है जो दस्तावेज़ पर चलता है और जब उपयोगकर्ता दस्तावेज़ में कर्सर ले जाता है तो हम नियंत्रण कक्ष पर बटन की स्थिति बदलते हैं।



कमान और नियंत्रक कार्यान्वयन

चूंकि बोल्ड कमांड शुरू में एपीआई द्वारा समर्थित है, हमारी कमांड ऑब्जेक्ट सिर्फ एक छोटा आवरण है।
 function Command(command, editDoc) { this.execute = function() { editDoc.execCommand(command, false, null); }; this.queryState = function() { return editDoc.queryCommandState(command) }; }
      
      



हमें एक रैपर की आवश्यकता क्यों है? चूँकि हम चाहते हैं कि हमारी अमानक टीमें मानक लोगों के समान ही हों।
हमारा बटन केवल एक स्पैन है:
 <span id="boldButton">Bold</span>
      
      



स्पान नियंत्रक के माध्यम से कमांड ऑब्जेक्ट के साथ जुड़ा हुआ है:
 function TogglCommandController(command, elem) { this.updateUI = function() { var state = command.queryState(); elem.className = state?"active":""; } bindEvent(elem, "click", function(evt) { command.execute(); updateToolbar(); }); }
      
      



नियंत्रण कक्ष पर बटन पर क्लिक करने पर संपादन विंडो द्वारा फ़ोकस बनाए रखने के लिए ज़िम्मेदार कोड को लिस्टिंग से बाहर कर दिया गया था। नीचे हम ToggleCommandController फ़ंक्शन को उनके दो राज्यों को ध्यान में रखते हुए बटन की स्थिति और पाठ की शैली को सिंक्रनाइज़ करने के लिए कहते हैं। जब एक बटन दबाया जाता है, तो एक कमांड निष्पादित होता है। जब अपडेटयूआई घटना को निकाल दिया जाता है, तो स्पैन को "सक्रिय" वर्ग मिलता है, या पाठ की स्थिति के आधार पर इसे खो देता है। सीएसएस गुण जो बटन की उपस्थिति निर्धारित करते हैं:
 .toolbar span { border: outset; } .toolbar span.active { border: inset; }
      
      



घटक निम्नानुसार जुड़े हुए हैं:
 var command = Command("Bold", editDoc); var elem = document.getElementById(îboldButton); var controller = new TogglCommandController(command, elem); updateListeners.push(controller);
      
      



UpdateListeners संग्रह में कंट्रोल पैनल के लिए नियंत्रक होते हैं। अपडेटटूलबार फ़ंक्शन सूची पर पुनरावृत्त करता है और प्रत्येक नियंत्रक के लिए अपडेटयूआई विधि को कॉल करता है ताकि सभी नियंत्रण बिल्कुल अद्यतित हों। हम घटनाओं को संलग्न करते हैं ताकि अपडेटटूलबार को हर बार दस्तावेज़ चयन परिवर्तन कहा जाता है:
 bindEvent(editDoc, "keyup", updateToolbar); bindEvent(editDoc, "mouseup", updateToolbar);
      
      



जैसा कि ऊपर दिखाया गया है, जब कमांड निष्पादित होता है तो अपडेटटूलबार को कहा जाता है। हम कमांड के साथ जुड़े बटन को अपडेट करने के बजाय, प्रत्येक कमांड को निष्पादित करने के बाद पूरे कंट्रोल पैनल को अपडेट क्यों कर रहे हैं? चूंकि कमांड निष्पादन के परिणामस्वरूप अन्य नियंत्रण के राज्य भी बदल सकते हैं। उदाहरण के लिए, यदि हम दाएं संरेखित कमांड का उपयोग करते हैं, तो बाएं संरेखित बटन की स्थिति और केंद्र बटन भी बदल जाएगा। सभी संभव निर्भरता पर नज़र रखने के बजाय, पूरे नियंत्रण कक्ष को अपडेट करना आसान है। अब हमारे पास दो राज्यों वाली टीमों के लिए एक बुनियादी इंटरफ़ेस है। परिणामी फ्रेमवर्क का उपयोग करते हुए, बोल्ड, इटैलिक, जस्टिफायलेट, जस्टिफ़ायर, और जस्टिफ़ाइकेंटर टीमें कार्यान्वित की जाती हैं।



लिंक

हमने मूल पाठ प्रारूपण आदेशों को लागू करने के बाद, मैंने उपयोगकर्ताओं को दस्तावेज़ में लिंक जोड़ने की क्षमता देने का फैसला किया। लिंक प्रबंधन को और अधिक जटिल तर्क की आवश्यकता है, क्योंकि createLink काम नहीं करता है जैसा हम चाहते हैं। यह एक लिंक बनाता है, लेकिन इस बारे में जानकारी वापस नहीं करता है कि चयन लिंक के अंदर है या नहीं। और हमें नियंत्रण कक्ष और चयन की स्थिति को सिंक्रनाइज़ करने के लिए इसकी आवश्यकता है। लिंक के अंदर चयन होने पर हम कैसे जांच सकते हैं? हम getContaining फ़ंक्शन को लिखकर ऐसा करेंगे, जो DOM ट्री में उस तत्व से अधिक बढ़ जाता है जिसमें कर्सर स्थित है, जब तक हमें आवश्यक प्रकार के माता-पिता नहीं मिलते हैं (इस मामले में लिंक। फ़ंक्शन तत्व नहीं मिलने पर कुछ भी नहीं लौटाता है)। यदि चयन एक टैग के अंदर है, तो हम लिंक के अंदर हैं। हमें लिंक के लिए URL के लिए उपयोगकर्ता से पूछने का एक तरीका भी चाहिए। एक कूलर संपादक इस अनुरोध के लिए एक गैर-मानक संवाद बनाएगा, लेकिन कार्य को सरल बनाने के लिए, हम सिर्फ मानक window.promet फ़ंक्शन का उपयोग करते हैं। यदि चयन लिंक के अंदर है, तो हम इसका वर्तमान URL दिखाएंगे ताकि उपयोगकर्ता इसे बदल सके। अन्यथा, हम बस http: // उपसर्ग दिखाते हैं। Linkcommand फ़ंक्शन कोड:
 function LinkCommand(editDoc) { var tagFilter = function(elem){ return elem.tagName=="A"; }; //(1) this.execute = function() { var a = getContaining(editWindow, tagFilter); //(2) var initialUrl = a ? a.href : "http://"; //(3) var url = window.prompt("Enter an URL:", initialUrl); if (url===null) return; //(4) if (url==="") { editDoc.execCommand("unlink", false, null); //(5) } else { editDoc.execCommand("createLink", false, url); //(6) } }; this.queryState = function() { return !!getContaining(editWindow, tagFilter); //(7) }; }
      
      



फ़ंक्शन का तर्क इस प्रकार है:
  1. फ़ंक्शन यह जांचता है कि क्या वर्तमान तत्व खोजा जाने वाला है। कोड में मामले की परवाह किए बिना, टैगनाम हमेशा अपरकेस में वापस आ जाता है।
  2. getContain तत्व दिए गए नाम के साथ तत्व की खोज करता है। यदि यह नहीं मिला है, तो अशक्त है।
  3. यदि मूल तत्वों के बीच कोई लिंक पाया जाता है, तो हम संवाद में href विशेषता जोड़ते हैं। अन्यथा, यह एक मानक http: // होगा।
  4. यदि उपयोगकर्ता क्लिक को रद्द करता है तो शीघ्र रिटर्न शून्य हो जाता है। इस स्थिति में, कमांड का निष्पादन समाप्त हो जाता है।
  5. यदि उपयोगकर्ता URL निकालता है और OK पर क्लिक करता है, तो हम मान लेते हैं कि उपयोगकर्ता लिंक को हटाना चाहता है। ऐसा करने के लिए, हम मानक अनलिंक कमांड का उपयोग करते हैं।
  6. यदि उपयोगकर्ता URL दर्ज करता है और OK पर क्लिक करता है, तो हम createLink कमांड का उपयोग करके लिंक बनाते हैं। (यदि लिंक पहले से मौजूद है, तो हम URL को एक नए के साथ बदल देते हैं)।
  7. एक बूलियन प्रकार में दोहरा नकार परिणाम - सच है अगर तत्व पाया जाता है और अन्यथा झूठ है।
  8. हम LinkCommand को मानक ToggleCommandController के साथ जोड़ सकते हैं, क्योंकि नियंत्रण कक्ष इंटरफ़ेस अपरिवर्तित रहता है: सभी समान निष्पादन और क्वेरीस्ट्रेट विधियाँ।

GetContaining

चलो getContain फ़ंक्शन (editlib.js में स्थित) को देखें। फ़ंक्शन यह जांचता है कि क्या चयन एक निश्चित प्रकार के तत्व के अंदर है। यह थोड़ा अधिक जटिल है, क्योंकि IE एपीआई अन्य ब्राउज़रों के एपीआई की तुलना में थोड़ा अलग काम करता है। इसलिए, हमें फ़ंक्शन के दो स्वतंत्र कार्यान्वयन और एक तंत्र बनाना होगा जो यह निर्धारित करता है कि किसका उपयोग किया जाना चाहिए - हम गेट्सलेक्शन संपत्ति की उपलब्धता का निर्धारण करके ऐसा करेंगे। इस तरह:
 var getContaining = (window.getSelection)?w3_getContaining:ie_getContaining;
      
      



IE में फ़ंक्शन का कार्यान्वयन अधिक दिलचस्प है, क्योंकि यह IE में चयन एपीआई की कुछ विशेषताओं को दर्शाता है।
 function ie_getContaining(editWindow, filter) { var selection = editWindow.document.selection; if (selection.type=="Control") { //(1) // control selection var range = selection.createRange(); if (range.length==1) { var elem = range.item(0); //(3) } else { // multiple control selection return null; //(2) } } else { var range = selection.createRange(); //(4) var elem = range.parentElement(); } return getAncestor(elem, filter); }
      
      



यह निम्नानुसार काम करता है:
  1. चयन वस्तु का प्रकार या तो "नियंत्रण" या "पाठ" है। कई ऑब्जेक्ट्स (नियंत्रण) का चयन किया जा सकता है (यानी, उपयोगकर्ता ctrl + क्लिक का उपयोग करके कई आसन्न छवियों का चयन कर सकता है)।
  2. हम कई चयनित वस्तुओं के साथ स्थिति को नहीं संभालेंगे; इस मामले में, हम बस कमांड को रद्द करते हैं और कुछ भी नहीं होता है।
  3. यदि हमारे पास चयन में एक वस्तु है, तो हम इसका चयन करते हैं।
  4. यदि चयन पाठात्मक है, तो हम कंटेनर प्राप्त करने के लिए इसका उपयोग करते हैं।
अन्य ब्राउज़रों द्वारा उपयोग किया जाने वाला एपीआई अपेक्षाकृत सरल है:
 function w3_getContaining(editWindow, filter) { var range = editWindow.getSelection().getRangeAt(0); //(1) var container = range.commonAncestorContainer; //(2) return getAncestor(container, filter); }
      
      



यह निम्नानुसार काम करता है:
  1. जबकि एपीआई कई चयन की अनुमति देता है, उपयोगकर्ता इंटरफ़ेस केवल एक की अनुमति देता है, इसलिए हम केवल पहली और एकमात्र श्रेणी पर विचार करते हैं।
  2. इस पद्धति में वह तत्व मिलता है जिसमें वर्तमान चयन होता है।
GetAncestor फ़ंक्शन सरल है - हम बस तत्वों के पदानुक्रम तक ऊपर जाते हैं जब तक हम पाते हैं कि हम क्या देख रहे हैं या जब तक हम पदानुक्रम के शीर्ष तक नहीं पहुंचते हैं, इस मामले में हम अशक्त हो जाते हैं:
 /* walks up the hierachy until an element with the tagName if found. Returns null if no element is found before BODY */ function getAncestor(elem, filter) { while (elem.tagName!="BODY") { if (filter(elem)) return elem; elem = elem.parentNode; } return null; }
      
      





कई मान लेते हैं

फ़ॉन्ट और आकार चयन जैसे संपादन तत्वों को थोड़ा अलग दृष्टिकोण की आवश्यकता होती है, क्योंकि उपयोगकर्ता मूल्यों के लिए कई विकल्पों का चयन कर सकता है। इसे लागू करने के लिए इंटरफ़ेस में, हमने पहले की तरह एक बटन के बजाय ड्रॉप-डाउन सूची का उपयोग किया। इसके अलावा, हमें कमांड और कंट्रोलर ऑब्जेक्ट्स को फिर से लिखना होगा, ताकि वे कई मानों के साथ काम कर सकें, न कि केवल बाइनरी स्टेट्स। यहां फ़ॉन्ट चुनने के लिए HTML कोड दिया गया है:
 <select id="fontSelector"> <option value="">Default</option> <option value="Courier">Courier</option> <option value="Verdana">Verdana</option> <option value="Georgia">Georgia</option> </select>
      
      



कमांड ऑब्जेक्ट अभी भी सरल है, क्योंकि यह मानक FontName कमांड में एक ऐड-ऑन है:
 function ValueCommand(command, editDoc) { this.execute = function(value) { editDoc.execCommand(command, false, value); }; this.queryValue = function() { return editDoc.queryCommandValue(command) }; }
      
      



ValueCommand और पहले वर्णित बाइनरी स्टेट कमांड के बीच का अंतर क्वेरीव्यू विधि है, जो एक स्ट्रिंग के रूप में वर्तमान मान लौटाता है। जब उपयोगकर्ता ड्रॉप-डाउन सूची से मान का चयन करता है, तो नियंत्रक कमांड को निष्पादित करता है।
 function ValueSelectorController(command, elem) { this.updateUI = function() { var value = command.queryValue(); elem.value = value; } bindEvent(elem, "change", function(evt) { editWindow.focus(); command.execute(elem.value); updateToolbar(); }); }
      
      



नियंत्रक काफी सरल है, क्योंकि हम ड्रॉप-डाउन सूची में मानों को कमांड के मानों के रूप में सीधे उपयोग करते हैं। फ़ॉन्ट आकार की ड्रॉप-डाउन सूची उसी तरह से काम करती है - हम अंतर्निहित फॉन्टसाइज़ कमांड का उपयोग करते हैं और उपलब्ध मानों के रूप में 1 से 7 तक आकारों का उपयोग करते हैं।



कस्टम टीम

अब तक, हमने HTML में मानक, अंतर्निहित कमांड का उपयोग करके सभी परिवर्तन किए हैं। लेकिन कभी-कभी HTML को बदलना आवश्यक हो सकता है जैसे कोई अंतर्निहित कमांड नहीं हो सकता। इस मामले में, हम DOM और रेंज API का उपयोग करते हैं। एक उदाहरण के रूप में, हम एक कमांड बनाएंगे जो इनपुट बिंदु पर कुछ HTML जोड़ देगा। चीजों को सरल रखने के लिए, यह केवल "हैलो वर्ल्ड" पाठ के साथ एक स्पैन होगा। लेकिन अगर आप किसी अन्य HTML को पेस्ट करना चाहते हैं तो दृष्टिकोण नहीं बदलेगा। कमांड इस तरह दिखाई देगी:
 function HelloWorldCommand() { this.execute = function() { var elem = editWindow.document.createElement("SPAN"); elem.style.backgroundColor = "red"; elem.innerHTML = "Hello world!"; overwriteWithNode(elem); } this.queryState = function() { return false; } }
      
      



OverwriteWithNode फ़ंक्शन में एक टोकन, जो वर्तमान इनपुट बिंदु पर एक तत्व सम्मिलित करता है। (विधि का नाम इंगित करता है कि यदि कोई गैर-रिक्त चयन है, तो इसकी सामग्री को ओवरराइट किया जाएगा)। IE और DOM रेंज मानक का समर्थन करने वाले ब्राउज़रों के बीच DOM अंतर के कारण, विधि को अलग तरीके से लागू किया जाता है। आइए सबसे पहले DOM रेंज के साथ काम करने वाले संस्करण पर विचार करें:
 function w3_overwriteWithNode(node) { var rng = editWindow.getSelection().getRangeAt(0); rng.deleteContents(); if (isTextNode(rng.startContainer)) { var refNode = rightPart(rng.startContainer, rng.startOffset) refNode.parentNode.insertBefore(node, refNode); } else { var refNode = rng.startContainer.childNodes[rng.startOffset]; rng.startContainer.insertBefore(node, refNode); } }
      
      



range.deleteContents, इसके नाम के अनुसार, चयन की सामग्री को हटा देता है, अगर यह पतित नहीं है। (यदि चयन पतित है, तो यह सिर्फ कुछ नहीं करता है)। DOM रेंज ऑब्जेक्ट में ऐसे गुण होते हैं जो हमें DOM में एक एंट्री पॉइंट को परिभाषित करने की अनुमति देते हैं: startContainer वह नोड होता है जिसमें एंट्री पॉइंट होता है और स्टार्टऑफ़सेट एक नंबर होता है जो पेरेंट नोड में एंट्री पॉइंट की स्थिति को इंगित करता है। उदाहरण के लिए, यदि startContainer एक तत्व है और startOffset तीन है, तो प्रवेश बिंदु तत्व के तीसरे और चौथे बच्चों के बीच है। यदि startContainer एक टेक्स्ट नोड है, तो startOffset का अर्थ माता-पिता की शुरुआत से वर्णों में ऑफसेट है। उदाहरण के लिए, 3 के बराबर स्टार्टऑफसेट का मतलब है कि प्रवेश बिंदु तीसरे और चौथे वर्ण के बीच है।



एंडकंटेनर और एंडऑफसेट उसी तरह से चयन के अंत का संकेत देते हैं। यदि चयन खाली (पतित) है, तो उनके पास स्टार्टऑनटेनर और स्टार्टऑफसेट के समान मूल्य है।




यदि प्रवेश बिंदु पाठ नोड के अंदर है, तो हमें इसे दो में विभाजित करना चाहिए ताकि हम उनके बीच अपना डेटा सम्मिलित कर सकें। राइटपार्ट एक ऐसा फंक्शन है जो सिर्फ इतना ही करता है - एक टेक्स्ट नोड को दो नोड्स में विभाजित करता है और इसके दाहिने हिस्से को वापस करता है। फिर हम इच्छित बिंदु पर नए नोड्स सम्मिलित करने के लिए InsertBefore का उपयोग कर सकते हैं। IE के लिए संस्करण थोड़ा पेचीदा है। IE में, एक रेंज ऑब्जेक्ट DOM में एक इनपुट बिंदु की स्थिति के बारे में जानकारी तक पहुंच प्रदान नहीं करता है। एक और समस्या यह है कि हम केवल पेस्टएचटीएमएल विधि का उपयोग करके डेटा सम्मिलित कर सकते हैं, जो एचटीएमएल को एक स्ट्रिंग के रूप में तर्क के रूप में लेता है, न कि डोम नोड के पेड़ के रूप में। सामान्य तौर पर, IE रेंज API को DOM API से पूरी तरह अलग किया जाता है! लेकिन एक चाल है जो हमें DOM एपीआई और IE रेंज एपीआई को साझा करने की अनुमति देगा: हम DOM में वांछित प्रविष्टि बिंदु खोजने के लिए एक अद्वितीय आईडी के साथ एक मार्कर तत्व डालने के लिए pasteHTML का उपयोग करते हैं:
 function ie_overwriteWithNode(node) { var range = editWindow.document.selection.createRange(); var marker = writeMarkerNode(range); marker.appendChild(node); marker.removeNode(); // removes node but not children } // writes a marker node on a range and returns the node. function writeMarkerNode(range) { var id = editWindow.document.uniqueID; var html = "<span id='" + id + "'></span>"; range.pasteHTML(html); var node = editWindow.document.getElementById(id); return node; }
      
      



कृपया ध्यान दें कि समाप्त होने के बाद हम मार्कर नोड को हटा देंगे। यह आवश्यक है ताकि HTML कोड को अव्यवस्थित न किया जाए। अब हमारे पास एक कमांड है जो चयन बिंदु में मनमाना HTML सम्मिलित करता है। उपयोगकर्ता इंटरफ़ेस के साथ इस क्रिया को जोड़ने के लिए हमने कंट्रोल पैनल और ToggleCommandController फ़ंक्शन पर बटन का उपयोग किया।



निष्कर्ष

इस लेख में, हमने HTML संपादक बनाने के लिए एक सरल रूपरेखा पर ध्यान दिया। अधिक जटिल संपादकों को विकसित करने के लिए कोड को एक टेम्पलेट के रूप में उपयोग किया जा सकता है।



All Articles