संतोषी इकाई के अंत में कर्सर को कैसे स्थानांतरित करें


86

मुझे contenteditableGmail नोट्स विजेट पर नोड के अंत में कैरेट को स्थानांतरित करने की आवश्यकता है ।

मैं स्टैकऑवरफ़्लो पर थ्रेड्स पढ़ता हूं, लेकिन वे समाधान इनपुट्स का उपयोग करने पर आधारित हैं और वे contenteditableतत्वों के साथ काम नहीं करते हैं।

जवाबों:


28

एक और समस्या भी है।

अगर एनको बर्न्स का समाधान काम करता है, अगर contenteditablediv में अन्य बहुस्तरीय तत्व नहीं हैं।

उदाहरण के लिए, यदि एक div में अन्य div हैं, और इन अन्य div में अन्य सामान हैं, तो कुछ समस्याएं हो सकती हैं।

उन्हें हल करने के लिए, मैंने निम्नलिखित समाधान की व्यवस्था की है, जो कि नीको के एक का सुधार है :

//Namespace management idea from http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/
(function( cursorManager ) {

    //From: http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
    var voidNodeTags = ['AREA', 'BASE', 'BR', 'COL', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR', 'BASEFONT', 'BGSOUND', 'FRAME', 'ISINDEX'];

    //From: /programming/237104/array-containsobj-in-javascript
    Array.prototype.contains = function(obj) {
        var i = this.length;
        while (i--) {
            if (this[i] === obj) {
                return true;
            }
        }
        return false;
    }

    //Basic idea from: /programming/19790442/test-if-an-element-can-contain-text
    function canContainText(node) {
        if(node.nodeType == 1) { //is an element node
            return !voidNodeTags.contains(node.nodeName);
        } else { //is not an element node
            return false;
        }
    };

    function getLastChildElement(el){
        var lc = el.lastChild;
        while(lc && lc.nodeType != 1) {
            if(lc.previousSibling)
                lc = lc.previousSibling;
            else
                break;
        }
        return lc;
    }

    //Based on Nico Burns's answer
    cursorManager.setEndOfContenteditable = function(contentEditableElement)
    {

        while(getLastChildElement(contentEditableElement) &&
              canContainText(getLastChildElement(contentEditableElement))) {
            contentEditableElement = getLastChildElement(contentEditableElement);
        }

        var range,selection;
        if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
        {    
            range = document.createRange();//Create a range (a range is a like the selection but invisible)
            range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
            range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
            selection = window.getSelection();//get the selection object (allows you to change selection)
            selection.removeAllRanges();//remove any selections already made
            selection.addRange(range);//make the range you have just created the visible selection
        }
        else if(document.selection)//IE 8 and lower
        { 
            range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
            range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
            range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
            range.select();//Select the range (make it the visible selection
        }
    }

}( window.cursorManager = window.cursorManager || {}));

उपयोग:

var editableDiv = document.getElementById("my_contentEditableDiv");
cursorManager.setEndOfContenteditable(editableDiv);

इस तरह, कर्सर निश्चित रूप से अंतिम तत्व के अंत में स्थित है, अंततः नेस्टेड है।

EDIT # 1 : अधिक सामान्य होने के लिए, जबकि कथन में उन सभी अन्य टैगों पर भी विचार करना चाहिए जिनमें पाठ नहीं हो सकता है। इन तत्वों को शून्य तत्व नाम दिया गया है , और इस प्रश्न में कुछ तरीके हैं कि किसी तत्व के शून्य होने पर कैसे परीक्षण किया जाए। इसलिए, यह मानते हुए कि एक फ़ंक्शन मौजूद है, canContainTextजो रिटर्न देता है trueयदि तर्क एक शून्य तत्व नहीं है, तो कोड की निम्न पंक्ति:

contentEditableElement.lastChild.tagName.toLowerCase() != 'br'

के साथ प्रतिस्थापित किया जाना चाहिए:

canContainText(getLastChildElement(contentEditableElement))

EDIT # 2 : उपरोक्त कोड पूरी तरह से अपडेट किया गया है, जिसमें वर्णित और चर्चा किए गए प्रत्येक परिवर्तन हैं


दिलचस्प है, मुझे उम्मीद थी कि ब्राउज़र इस मामले की स्वचालित रूप से देखभाल करेगा (यह नहीं कि मुझे आश्चर्य है कि ऐसा नहीं होता है, ब्राउज़र कभी भी संतोषप्रद चीजों को सहज नहीं करते हैं)। क्या आपके पास HTML का एक उदाहरण है जहां आपका समाधान काम करता है लेकिन मेरा नहीं है?
निको बर्न्स

मेरे कोड में एक और त्रुटि थी। मैंने ठीक कर दिया। अब, आप यह सत्यापित कर सकते हैं कि इस पृष्ठ में मेरा कोड काम करता है , जबकि आपका नहीं
वीटो जेंटाइल

मुझे आपके फ़ंक्शन का उपयोग करने में त्रुटि हो रही है, कंसोल कहता है Uncaught TypeError: Cannot read property 'nodeType' of nullऔर यह getLastChildElement फ़ंक्शन से बुलाया जा रहा है। क्या आप जानते हैं कि इस समस्या के कारण क्या हो सकते हैं?
डेरेक

@ वीटोगेंटाइल यह थोड़ा पुराना उत्तर है, लेकिन मैं ध्यान देना चाहता हूं कि आपका समाधान केवल ब्लॉक तत्वों का ध्यान रखता है, अगर अंदर लाइन तत्व हैं, तो कर्सर को इनलाइन तत्व (जैसे स्पैन, एमएम ...) के बाद स्थित किया जाएगा। , एक आसान समाधान इनलाइन तत्वों को शून्य टैग के रूप में मानना ​​है और उन्हें voidNodeTags में जोड़ना है ताकि उन्हें छोड़ दिया जाए।
medBouzid

242

Geowa4 का समाधान एक textarea के लिए काम करेगा, लेकिन एक संतुष्ट तत्व के लिए नहीं।

यह समाधान कैरेट को एक संतोषप्रद तत्व के अंत तक ले जाने के लिए है। यह सभी ब्राउज़रों में काम करना चाहिए जो संतोषप्रद समर्थन करते हैं।

function setEndOfContenteditable(contentEditableElement)
{
    var range,selection;
    if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
    {
        range = document.createRange();//Create a range (a range is a like the selection but invisible)
        range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
        range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
        selection = window.getSelection();//get the selection object (allows you to change selection)
        selection.removeAllRanges();//remove any selections already made
        selection.addRange(range);//make the range you have just created the visible selection
    }
    else if(document.selection)//IE 8 and lower
    { 
        range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
        range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
        range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
        range.select();//Select the range (make it the visible selection
    }
}

इसका उपयोग कोड के समान किया जा सकता है:

elem = document.getElementById('txt1');//This is the element that you want to move the caret to the end of
setEndOfContenteditable(elem);

1
जियोवा 4 का सॉल्यूशन क्रोमरा में टेक्स्टरी के लिए काम करेगा, यह किसी भी ब्राउजर में कंटेडटेबल एलिमेंट्स के लिए काम नहीं करेगा। खान संतोषी तत्वों के लिए काम करता है, लेकिन बनावट के लिए नहीं।
निको बर्न्स

4
इस सवाल का सही जवाब है, एकदम सही, धन्यवाद Nico।
रोब

7
selectNodeContentsनिको के दोनों क्रोम और एफएफ में मुझे त्रुटियों दे रहा था का हिस्सा (अन्य ब्राउज़रों का परीक्षण नहीं किया है) जब तक मुझे पता चला कि मैं जाहिरा तौर पर जोड़ने के लिए आवश्यक .get(0)तत्व है कि मैं समारोह खिला था। मुझे लगता है कि यह मेरे साथ नंगे जेएस के बजाय jQuery का उपयोग करना है? मैंने प्रश्न ४२३३२६५ में @jwarcheng से यह सीखा । सभी को धन्यवाद!
मैक्स स्टार्कबर्ग 2

5
हां, फ़ंक्शन को एक DOM तत्व की अपेक्षा है, एक jQuery ऑब्जेक्ट की नहीं। .get(0)डोम तत्व को पुनः प्राप्त करता है जो jQuery आंतरिक रूप से संग्रहीत करता है। आप भी जोड़ सकते हैं [0], जो .get(0)इस संदर्भ में बराबर है ।
निको बर्न्स

1
@ निको बर्न्स: मैंने आपके तरीके की कोशिश की और यह फ़ायरफ़ॉक्स पर काम नहीं किया।
लेविस

26

यदि आप पुराने ब्राउज़रों की परवाह नहीं करते हैं, तो इसने मेरे लिए चाल चली।

// [optional] make sure focus is on the element
yourContentEditableElement.focus();
// select all the content in the element
document.execCommand('selectAll', false, null);
// collapse selection to the end
document.getSelection().collapseToEnd();

यह एकमात्र चीज है जिसने मेरे लिए क्रोम एक्सटेंशन के लिए पृष्ठभूमि स्क्रिप्ट के अंदर काम किया
रोब

1
यह ठीक काम करता है। एंड्रॉइड 5.1 पर क्रोम 71.0.3578.98 और वेबव्यू पर परीक्षण किया गया।
मासवर्द्धन

3
document.execCommandअब अप्रचलित है developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
वेबप्रोग्रामर

2020 और यह अभी भी क्रोम संस्करण 83.0.4103.116 (आधिकारिक बिल्ड) (64-बिट) में काम करता है
user2677034

यह विजेता है!
एमएनएन टीएनके

8

श्रेणी के अंत में कर्सर सेट करना संभव है:

setCaretToEnd(target/*: HTMLDivElement*/) {
  const range = document.createRange();
  const sel = window.getSelection();
  range.selectNodeContents(target);
  range.collapse(false);
  sel.removeAllRanges();
  sel.addRange(range);
  target.focus();
  range.detach(); // optimization

  // set scroll to the end if multiline
  target.scrollTop = target.scrollHeight; 
}

ऊपर दिए गए कोड का उपयोग करने से यह ट्रिक काम करता है - लेकिन मैं तब सक्षम होना चाहता हूं जब सामग्री संपादन योग्य डिव के भीतर कहीं भी कर्सर ले जाने और उस बिंदु से टाइपिंग जारी रखने की क्षमता हो - उदाहरण के लिए उपयोगकर्ता ने टाइपो को पहचान लिया है ... कैसे होगा मैं इससे ऊपर आपके कोड में संशोधन करता हूं?
11:16 पर ज़ैब

1
@Zabs यह काफी आसान है: setCaretToEnd()हर बार चालान न करें - इसे केवल तब ही लागू करें जब आपको इसकी आवश्यकता हो: जैसे कॉपी-पेस्ट के बाद, या संदेश की लंबाई को सीमित करने के बाद।
am0wa

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

0

मुझे एक तत्व को संपादन योग्य बनाने की कोशिश में एक समान समस्या थी। क्रोम और फायरफॉक्स में यह संभव था लेकिन फायरफॉक्स में कैरेट या तो इनपुट की शुरुआत में चला गया या फिर इनपुट खत्म होने के बाद यह एक जगह चला गया। मुझे लगता है कि अंत उपयोगकर्ता के लिए बहुत भ्रामक, सामग्री को संपादित करने की कोशिश कर रहा है।

मुझे कई चीजों की कोशिश करने का कोई हल नहीं मिला। केवल एक चीज जो मेरे लिए काम करती थी, वह थी "पुरानी समस्या का समाधान करना", एक सादा पुराना पाठ-इनपुट इनसाइड माय डालकर। अब यह काम कर रहा है। "कंटेंट-एडिटेबल" की तरह लगता है कि अभी भी एज तकनीक से खून बह रहा है, जो काम कर सकता है या नहीं जैसा कि आप इसे पसंद करेंगे, संदर्भ के आधार पर।


0

फ़ोकस इवेंट के जवाब में संपादन योग्य अवधि के अंत में कर्सर ले जाना:

  moveCursorToEnd(el){
    if(el.innerText && document.createRange)
    {
      window.setTimeout(() =>
        {
          let selection = document.getSelection();
          let range = document.createRange();

          range.setStart(el.childNodes[0],el.innerText.length);
          range.collapse(true);
          selection.removeAllRanges();
          selection.addRange(range);
        }
      ,1);
    }
  }

और इसे इवेंट हैंडलर में बुलाएं (यहां प्रतिक्रिया दें):

onFocus={(e) => this.moveCursorToEnd(e.target)}} 

0

के साथ समस्या contenteditable <div>और <span>हल हो गई है जब आप शुरू में यह में लिखना प्रारंभ करें। इसके लिए एक वर्कअराउंड आपके div एलिमेंट पर फोकस इवेंट को ट्रिगर कर सकता है और उस फंक्शन पर, क्लियर और रीफिल कर सकता है जो पहले से ही div एलिमेंट में था। इस तरह से समस्या हल हो गई है और अंत में आप रेंज और चयन का उपयोग करके कर्सर को अंत में रख सकते हैं। मेरे लिए काम किया।

  moveCursorToEnd(e : any) {
    let placeholderText = e.target.innerText;
    e.target.innerText = '';
    e.target.innerText = placeholderText;

    if(e.target.innerText && document.createRange)
    {
      let range = document.createRange();
      let selection = window.getSelection();
      range.selectNodeContents(e.target);
      range.setStart(e.target.firstChild,e.target.innerText.length);
      range.setEnd(e.target.firstChild,e.target.innerText.length);
      selection.removeAllRanges();
      selection.addRange(range);
    }
  }

HTML कोड में:

<div contentEditable="true" (focus)="moveCursorToEnd($event)"></div>
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.