সন্তোষজনক উপাদান (ডিভ) মধ্যে ক্যারেট (কার্সার) অবস্থান কীভাবে সেট করবেন?


190

উদাহরণ হিসাবে আমার কাছে এই সাধারণ এইচটিএমএল রয়েছে:

<div id="editable" contenteditable="true">
  text text text<br>
  text text text<br>
  text text text<br>
</div>
<button id="button">focus</button>

আমি সাধারণ জিনিসটি চাই - আমি যখন বোতামটি ক্লিক করি তখন সম্পাদনাযোগ্য ডিভের মধ্যে ক্যারেট (কার্সার) নির্দিষ্ট জায়গায় রাখতে চাই। ওয়েবে অনুসন্ধান করা থেকে, আমার কাছে এই জেএস বোতাম ক্লিকের সাথে সংযুক্ত রয়েছে, তবে এটি কার্যকর হয় না (এফএফ, ক্রোম):

var range = document.createRange();
var myDiv = document.getElementById("editable");
range.setStart(myDiv, 5);
range.setEnd(myDiv, 5);

এইভাবে ম্যানুয়ালি ক্যারেট অবস্থান নির্ধারণ করা কি সম্ভব?

উত্তর:


258

বেশিরভাগ ব্রাউজারগুলিতে আপনার প্রয়োজন Rangeএবং Selectionঅবজেক্টগুলির প্রয়োজন । আপনি প্রতিটি নির্বাচনের সীমানা নোড এবং সেই নোডের মধ্যে একটি অফসেট হিসাবে নির্দিষ্ট করেন। উদাহরণস্বরূপ, পাঠ্য দ্বিতীয় লাইনের পঞ্চম অক্ষরে ক্যারেট সেট করতে, আপনি নিম্নলিখিতটি করতে চাই:

var el = document.getElementById("editable");
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el.childNodes[2], 5);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);

আইই <9 সম্পূর্ণ ভিন্নভাবে কাজ করে। আপনার যদি এই ব্রাউজারগুলিকে সমর্থন করতে হয় তবে আপনার আলাদা কোড দরকার।

জেএসফিডাল উদাহরণ: http://jsfiddle.net/timdown/vXnCM/


2
আপনার সমাধান নিখুঁতভাবে কাজ করে। অনেক ধন্যবাদ. "পাঠ্য প্রসঙ্গে" কাজ করার কোনও সুযোগ রয়েছে কি - এর অর্থ পজিশন # 5 একটি পর্দার পঞ্চম অক্ষর এবং কোনও কোডের পঞ্চম বর্ণ নয়?
ফ্রডিক

3
@Frodik: আপনি ব্যবহার করতে পারে setSelectionRange(): উত্তর আমি এখানে লিখেছিলেন থেকে ফাংশন stackoverflow.com/questions/6240139/... । আমি উত্তরে উল্লেখ করেছি, বিভিন্ন জিনিস রয়েছে যা সঠিকভাবে / ধারাবাহিকভাবে পরিচালনা করবে না তবে এটি যথেষ্ট ভাল হতে পারে।
টিম ডাউন

7
স্প্যান ট্যাগের অভ্যন্তরে ক্যারেটটি কীভাবে সেট করবেন: << ডিভি আইডি = "সম্পাদনযোগ্য" কনটেনটেটেবল = "সত্য"> টেস্ট 1 <br> টেস্ট 2 <br> <স্প্যান> </span> </div>
মেড আকরাম জেড

1
@ ম্যালকমম ওশান: বার্ফ, কারণ আই <9 এর কোনও নেই document.createRange(বা window.getSelection, তবে এটি এত দিন পাবে না)।
টিম ডাউন

1
@ অ্যান্ড্রয়েড: ম্যাকের ফায়ারফক্স ৩৮.০.৫-তে আমার জন্য জিসফিল ভাল কাজ করে।
টিম ডাউন

62

সন্তোষজনক কার্সার পজিশনিং-এ আপনি বেশিরভাগ উত্তরগুলি খুঁজে পান তা মোটামুটি সরল যে এগুলি কেবল সরল ভ্যানিলা পাঠ্যের সাথে ইনপুটগুলি সরবরাহ করে। একবার আপনি ধারকটির মধ্যে এইচটিএমএল উপাদান ব্যবহার করার পরে প্রবেশ করা পাঠ্যটি নোডে বিভক্ত হয়ে গাছের কাঠামোতে উদারভাবে বিতরণ করা হবে।

কার্সারের অবস্থান নির্ধারণ করতে আমার এই ফাংশনটি রয়েছে যা সরবরাহকৃত নোডের মধ্যে সমস্ত শিশু পাঠ্য নোডের চারপাশে লুপ করে এবং প্রাথমিক নোডের শুরু থেকে শুরু করে চরসকন্টের অক্ষর পর্যন্ত একটি সীমা নির্ধারণ করে :

function createRange(node, chars, range) {
    if (!range) {
        range = document.createRange()
        range.selectNode(node);
        range.setStart(node, 0);
    }

    if (chars.count === 0) {
        range.setEnd(node, chars.count);
    } else if (node && chars.count >0) {
        if (node.nodeType === Node.TEXT_NODE) {
            if (node.textContent.length < chars.count) {
                chars.count -= node.textContent.length;
            } else {
                range.setEnd(node, chars.count);
                chars.count = 0;
            }
        } else {
           for (var lp = 0; lp < node.childNodes.length; lp++) {
                range = createRange(node.childNodes[lp], chars, range);

                if (chars.count === 0) {
                    break;
                }
            }
        }
    } 

    return range;
};

আমি তারপর এই ফাংশন দিয়ে রুটিন কল:

function setCurrentCursorPosition(chars) {
    if (chars >= 0) {
        var selection = window.getSelection();

        range = createRange(document.getElementById("test").parentNode, { count: chars });

        if (range) {
            range.collapse(false);
            selection.removeAllRanges();
            selection.addRange(range);
        }
    }
};

রেঞ্জকোলপ্লেস (ভুয়া) রেঞ্জের শেষে কার্সার সেট করে। আমি এটি Chrome এর সর্বশেষতম সংস্করণ, আইই, মজিলা এবং অপেরা দিয়ে পরীক্ষা করেছি এবং তারা সকলেই ভাল কাজ করে।

পুনশ্চ. যদি কারও আগ্রহী আমি এই কোডটি ব্যবহার করে বর্তমান কার্সারের অবস্থানটি পাই:

function isChildOf(node, parentId) {
    while (node !== null) {
        if (node.id === parentId) {
            return true;
        }
        node = node.parentNode;
    }

    return false;
};

function getCurrentCursorPosition(parentId) {
    var selection = window.getSelection(),
        charCount = -1,
        node;

    if (selection.focusNode) {
        if (isChildOf(selection.focusNode, parentId)) {
            node = selection.focusNode; 
            charCount = selection.focusOffset;

            while (node) {
                if (node.id === parentId) {
                    break;
                }

                if (node.previousSibling) {
                    node = node.previousSibling;
                    charCount += node.textContent.length;
                } else {
                     node = node.parentNode;
                     if (node === null) {
                         break
                     }
                }
           }
      }
   }

    return charCount;
};

কোডটি সেট ফাংশনের বিপরীতে কাজ করে - এটি বর্তমান উইন্ডোটিসবেটলেশন () পায় focus IsChildOf ফাংশনটি চলার আগে কেবল পরীক্ষা করে যে সরবরাহিত নোড আসলে সরবরাহকৃত প্যারেন্টআইডির একটি শিশু ।

কোড পরিবর্তন ছাড়া সোজা কাজ করা উচিত, কিন্তু আমি শুধু একটি jQuery প্লাগিন আমি তাই তৈরি করেছি একটি দম্পতি আউট গভীর ক্ষত থেকে এটা নিয়েছি এই এর - আমাকে জানতে যদি কিছু কাজ না করে দিন!


1
আপনি কি এই কাজের একটি jsfiddle প্রদান করতে পারেন দয়া করে? আমি উদাহরণস্বরূপ কী node.idএবং এর parentIdসাথে সম্পর্কিত তা নিশ্চিত না হওয়ায় এটি কীভাবে কাজ করে তা নির্ধারণের জন্য আমি সংগ্রাম করছি । ধন্যবাদ :)
বেন্দিহসান

4
@ বেনদিহসান - এই jsfiddle.net/nrx9yvw9/5 চেষ্টা করুন - কিছু কারণে এই উদাহরণে লিখিত সামগ্রী সম্পাদনাযোগ্য ডিভ কিছু অক্ষর যুক্ত করছে এবং পাঠ্যের শুরুতে একটি ক্যারেজ ফিরছে (এটি এমনকি জিসফিল নিজেই এটি করছেন যেমন এটি করছেন) আমার টিপ সার্ভারে একই কাজ করবেন না)।
Liam

@ বেনডিহসান - কনটেনটেটেবল ডিভের মধ্যে থাকা এইচটিএমএল উপাদানগুলি প্রতিটি এইচটিএমএল উপাদানগুলির জন্য একটি নোড দিয়ে একটি গাছের কাঠামোতে ভেঙে যায়। GetCurrentCursorPosition বর্তমান নির্বাচনের অবস্থান পায় এবং গাছটি কত ব্যাক আপ টেক্সট অক্ষর আছে তা গণনা করে ফিরে যায়। নোড.আইডি এইচটিএমএল উপাদান আইডি, যখন প্যারেন্টআইডি এইচটিএমএল উপাদান আইডি উল্লেখ করে তবে এটি গণনা বন্ধ করে দেওয়া উচিত
লিয়াম

1
আমার ইউআই কোড থেকে সম্পূর্ণ পৃথক এমন একটি লিখতে এটি আমার টোড তালিকায় রয়েছে - আমার যখন সেকেন্ড থাকে আমি তা পোস্ট করব।
লিয়াম

1
আপনার বিভিন্ন সমাধানগুলি দ্রুত পরীক্ষা করতে সক্ষম হতে, আপনি কি নিজের উত্তরটি রান্নেবল কোড স্নিপেটে সম্পাদনা করতে পারবেন? তুমাকে অগ্রিম ধন্যবাদ.
বাসজ

3

আপনি jQuery ব্যবহার করতে না চাইলে আপনি এই পদ্ধতির চেষ্টা করতে পারেন:

public setCaretPosition() {
    const editableDiv = document.getElementById('contenteditablediv');
    const lastLine = this.input.nativeElement.innerHTML.replace(/.*?(<br>)/g, '');
    const selection = window.getSelection();
    selection.collapse(editableDiv.childNodes[editableDiv.childNodes.length - 1], lastLine.length);
}

editableDivআপনি এডিটযোগ্য উপাদান, idএটির জন্য একটি সেট করতে ভুলবেন না। তারপরে আপনাকে innerHTMLউপাদানটি থেকে বের হয়ে সমস্ত ব্রেক লাইন কেটে নেওয়া দরকার। এবং স্রেফ পরবর্তী যুক্তি দিয়ে পতন সেট করুন।


3
  const el = document.getElementById("editable");
  el.focus()
  let char = 1, sel; // character at which to place caret

  if (document.selection) {
    sel = document.selection.createRange();
    sel.moveStart('character', char);
    sel.select();
  }
  else {
    sel = window.getSelection();
    sel.collapse(el.lastChild, char);
  }

3

function set_mouse() {
  var as = document.getElementById("editable");
  el = as.childNodes[1].childNodes[0]; //goal is to get ('we') id to write (object Text) because it work only in object text
  var range = document.createRange();
  var sel = window.getSelection();
  range.setStart(el, 1);
  range.collapse(true);
  sel.removeAllRanges();
  sel.addRange(range);

  document.getElementById("we").innerHTML = el; // see out put of we id
}
<div id="editable" contenteditable="true">dddddddddddddddddddddddddddd
  <p>dd</p>psss
  <p>dd</p>
  <p>dd</p>
  <p>text text text</p>
</div>
<p id='we'></p>
<button onclick="set_mouse()">focus</button>

আপনার যখন অগ্রিম উপাদান যেমন (পি) (স্প্যান) ইত্যাদি থাকে তখন এটি যথাযথ অবস্থানে খুব শক্ত সেট ক্যারেট হয় The লক্ষ্যটি (বস্তুর পাঠ্য) পাওয়া:

<div id="editable" contenteditable="true">dddddddddddddddddddddddddddd<p>dd</p>psss<p>dd</p>
    <p>dd</p>
    <p>text text text</p>
</div>
<p id='we'></p>
<button onclick="set_mouse()">focus</button>
<script>

    function set_mouse() {
        var as = document.getElementById("editable");
        el = as.childNodes[1].childNodes[0];//goal is to get ('we') id to write (object Text) because it work only in object text
        var range = document.createRange();
        var sel = window.getSelection();
        range.setStart(el, 1);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);

        document.getElementById("we").innerHTML = el;// see out put of we id
    }
</script>

1
আপনার উত্তরটি দ্রুত পরীক্ষা করতে সক্ষম হওয়ার জন্য, আপনি কি নিজের উত্তরটি একটি চলমান কোড স্নিপেটে সম্পাদনা করতে পারবেন? তুমাকে অগ্রিম ধন্যবাদ.
বাসজ

1

আমি একটি সিনট্যাক্স হাইলাইটার লিখছি (এবং বেসিক কোড এডিটর), এবং আমার কীভাবে একটি একক উদ্ধৃতি চরকে স্বয়ংক্রিয়ভাবে টাইপ করতে হবে এবং ক্যারেটটি পিছনে স্থানান্তরিত করতে হবে (আজকাল প্রচুর কোড সম্পাদকদের মতো) know

আমার সমাধানের স্নিপেট হেরিস, এই থ্রেড, এমডিএন ডক্স এবং মোজ কনসোল দেখার জন্য প্রচুর সহায়তার জন্য ধন্যবাদ ..

//onKeyPress event

if (evt.key === "\"") {
    let sel = window.getSelection();
    let offset = sel.focusOffset;
    let focus = sel.focusNode;

    focus.textContent += "\""; //setting div's innerText directly creates new
    //nodes, which invalidate our selections, so we modify the focusNode directly

    let range = document.createRange();
    range.selectNode(focus);
    range.setStart(focus, offset);

    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
}

//end onKeyPress event

এটি একটি বিতর্কিত ডিভ উপাদান

ইতিমধ্যে একটি গ্রহণযোগ্য উত্তর আছে বুঝতে পেরে আমি এটি একটি ধন্যবাদ হিসাবে এখানে রেখেছি।


1

আমি এটি আমার সাধারণ পাঠ্য সম্পাদকের জন্য তৈরি করেছি।

অন্যান্য পদ্ধতি থেকে পার্থক্য:

  • উচ্চ পারদর্শিতা
  • সমস্ত স্থান দিয়ে কাজ করে

ব্যবহার

// get current selection
const [start, end] = getSelectionOffset(container)

// change container html
container.innerHTML = newHtml

// restore selection
setSelectionOffset(container, start, end)

// use this instead innerText for get text with keep all spaces
const innerText = getInnerText(container)
const textBeforeCaret = innerText.substring(0, start)
const textAfterCaret = innerText.substring(start)

selection.ts

/** return true if node found */
function searchNode(
    container: Node,
    startNode: Node,
    predicate: (node: Node) => boolean,
    excludeSibling?: boolean,
): boolean {
    if (predicate(startNode as Text)) {
        return true
    }

    for (let i = 0, len = startNode.childNodes.length; i < len; i++) {
        if (searchNode(startNode, startNode.childNodes[i], predicate, true)) {
            return true
        }
    }

    if (!excludeSibling) {
        let parentNode = startNode
        while (parentNode && parentNode !== container) {
            let nextSibling = parentNode.nextSibling
            while (nextSibling) {
                if (searchNode(container, nextSibling, predicate, true)) {
                    return true
                }
                nextSibling = nextSibling.nextSibling
            }
            parentNode = parentNode.parentNode
        }
    }

    return false
}

function createRange(container: Node, start: number, end: number): Range {
    let startNode
    searchNode(container, container, node => {
        if (node.nodeType === Node.TEXT_NODE) {
            const dataLength = (node as Text).data.length
            if (start <= dataLength) {
                startNode = node
                return true
            }
            start -= dataLength
            end -= dataLength
            return false
        }
    })

    let endNode
    if (startNode) {
        searchNode(container, startNode, node => {
            if (node.nodeType === Node.TEXT_NODE) {
                const dataLength = (node as Text).data.length
                if (end <= dataLength) {
                    endNode = node
                    return true
                }
                end -= dataLength
                return false
            }
        })
    }

    const range = document.createRange()
    if (startNode) {
        if (start < startNode.data.length) {
            range.setStart(startNode, start)
        } else {
            range.setStartAfter(startNode)
        }
    } else {
        if (start === 0) {
            range.setStart(container, 0)
        } else {
            range.setStartAfter(container)
        }
    }

    if (endNode) {
        if (end < endNode.data.length) {
            range.setEnd(endNode, end)
        } else {
            range.setEndAfter(endNode)
        }
    } else {
        if (end === 0) {
            range.setEnd(container, 0)
        } else {
            range.setEndAfter(container)
        }
    }

    return range
}

export function setSelectionOffset(node: Node, start: number, end: number) {
    const range = createRange(node, start, end)
    const selection = window.getSelection()
    selection.removeAllRanges()
    selection.addRange(range)
}

function hasChild(container: Node, node: Node): boolean {
    while (node) {
        if (node === container) {
            return true
        }
        node = node.parentNode
    }

    return false
}

function getAbsoluteOffset(container: Node, offset: number) {
    if (container.nodeType === Node.TEXT_NODE) {
        return offset
    }

    let absoluteOffset = 0
    for (let i = 0, len = Math.min(container.childNodes.length, offset); i < len; i++) {
        const childNode = container.childNodes[i]
        searchNode(childNode, childNode, node => {
            if (node.nodeType === Node.TEXT_NODE) {
                absoluteOffset += (node as Text).data.length
            }
            return false
        })
    }

    return absoluteOffset
}

export function getSelectionOffset(container: Node): [number, number] {
    let start = 0
    let end = 0

    const selection = window.getSelection()
    for (let i = 0, len = selection.rangeCount; i < len; i++) {
        const range = selection.getRangeAt(i)
        if (range.intersectsNode(container)) {
            const startNode = range.startContainer
            searchNode(container, container, node => {
                if (startNode === node) {
                    start += getAbsoluteOffset(node, range.startOffset)
                    return true
                }

                const dataLength = node.nodeType === Node.TEXT_NODE
                    ? (node as Text).data.length
                    : 0

                start += dataLength
                end += dataLength

                return false
            })

            const endNode = range.endContainer
            searchNode(container, startNode, node => {
                if (endNode === node) {
                    end += getAbsoluteOffset(node, range.endOffset)
                    return true
                }

                const dataLength = node.nodeType === Node.TEXT_NODE
                    ? (node as Text).data.length
                    : 0

                end += dataLength

                return false
            })

            break
        }
    }

    return [start, end]
}

export function getInnerText(container: Node) {
    const buffer = []
    searchNode(container, container, node => {
        if (node.nodeType === Node.TEXT_NODE) {
            buffer.push((node as Text).data)
        }
        return false
    })
    return buffer.join('')
}

1

আমি @ লিয়ামের উত্তরটি রিফ্যাক্ট করেছি। আমি এটিকে স্থিতিশীল পদ্ধতি সহ একটি শ্রেণীতে রেখেছি, আমি এর ক্রিয়াকলাপগুলিকে একটি # আইডির পরিবর্তে একটি উপাদান এবং কিছু অন্যান্য ছোট টুইটগুলি গ্রহণ করেছি।

এই কোডটি আপনার দ্বারা তৈরি করা সমৃদ্ধ পাঠ্য বাক্সে কার্সার ঠিক করার জন্য বিশেষভাবে ভাল <div contenteditable="true">। নীচের কোডে আসার আগে আমি বেশ কয়েক দিন ধরে আটকে ছিলাম।

সম্পাদনা করুন: তার উত্তর এবং এই উত্তরে হিট এন্টার জড়িত একটি বাগ রয়েছে। যেহেতু এন্টারটি একটি অক্ষর হিসাবে গণনা করা হয় না তাই প্রবেশ করানোর পরে কার্সার অবস্থান গণ্ডগোল হয়ে যায়। আমি কোডটি ঠিক করতে সক্ষম হলে, আমি আমার উত্তর আপডেট করব।

edit2: সংরক্ষণ করুন নিজেকে মাথাব্যাথা অনেক এবং আপনার করা <div contenteditable=true>হয় display: inline-block। এটি আপনি এন্টার টিপানোর <div>পরিবর্তে Chrome চাপানোর সাথে সম্পর্কিত কিছু বাগগুলি ঠিক করে <br>

ব্যবহারবিধি

let richText = document.getElementById('rich-text');
let offset = Cursor.getCurrentCursorPosition(richText);
// do stuff to the innerHTML, such as adding/removing <span> tags
Cursor.setCurrentCursorPosition(offset, richText);
richText.focus();

কোড

// Credit to Liam (Stack Overflow)
// https://stackoverflow.com/a/41034697/3480193
class Cursor {
    static getCurrentCursorPosition(parentElement) {
        var selection = window.getSelection(),
            charCount = -1,
            node;
        
        if (selection.focusNode) {
            if (Cursor._isChildOf(selection.focusNode, parentElement)) {
                node = selection.focusNode; 
                charCount = selection.focusOffset;
                
                while (node) {
                    if (node === parentElement) {
                        break;
                    }

                    if (node.previousSibling) {
                        node = node.previousSibling;
                        charCount += node.textContent.length;
                    } else {
                        node = node.parentNode;
                        if (node === null) {
                            break;
                        }
                    }
                }
            }
        }
        
        return charCount;
    }
    
    static setCurrentCursorPosition(chars, element) {
        if (chars >= 0) {
            var selection = window.getSelection();
            
            let range = Cursor._createRange(element, { count: chars });

            if (range) {
                range.collapse(false);
                selection.removeAllRanges();
                selection.addRange(range);
            }
        }
    }
    
    static _createRange(node, chars, range) {
        if (!range) {
            range = document.createRange()
            range.selectNode(node);
            range.setStart(node, 0);
        }

        if (chars.count === 0) {
            range.setEnd(node, chars.count);
        } else if (node && chars.count >0) {
            if (node.nodeType === Node.TEXT_NODE) {
                if (node.textContent.length < chars.count) {
                    chars.count -= node.textContent.length;
                } else {
                    range.setEnd(node, chars.count);
                    chars.count = 0;
                }
            } else {
                for (var lp = 0; lp < node.childNodes.length; lp++) {
                    range = Cursor._createRange(node.childNodes[lp], chars, range);

                    if (chars.count === 0) {
                    break;
                    }
                }
            }
        } 

        return range;
    }
    
    static _isChildOf(node, parentElement) {
        while (node !== null) {
            if (node === parentElement) {
                return true;
            }
            node = node.parentNode;
        }

        return false;
    }
}

0

আমি মনে করি সন্তুষ্ট উপাদানগুলির কিছু অবস্থানের জন্য ক্যারেট সেট করা সহজ নয়। আমি এই জন্য আমার নিজস্ব কোড লিখেছি। এটি নোড ট্রিকে কাস্টিং দিয়ে বাইপাস দেয় যে কয়টি অক্ষর রেখে গেছে এবং প্রয়োজনীয় উপাদানগুলিতে ক্যারেট সেট করে। আমি এই কোডটি বেশি পরীক্ষা করিনি।

//Set offset in current contenteditable field (for start by default or for with forEnd=true)
function setCurSelectionOffset(offset, forEnd = false) {
    const sel = window.getSelection();
    if (sel.rangeCount !== 1 || !document.activeElement) return;

    const firstRange = sel.getRangeAt(0);

    if (offset > 0) {
        bypassChildNodes(document.activeElement, offset);
    }else{
        if (forEnd)
            firstRange.setEnd(document.activeElement, 0);
        else
            firstRange.setStart(document.activeElement, 0);
    }



    //Bypass in depth
    function bypassChildNodes(el, leftOffset) {
        const childNodes = el.childNodes;

        for (let i = 0; i < childNodes.length && leftOffset; i++) {
            const childNode = childNodes[i];

            if (childNode.nodeType === 3) {
                const curLen = childNode.textContent.length;

                if (curLen >= leftOffset) {
                    if (forEnd)
                        firstRange.setEnd(childNode, leftOffset);
                    else
                        firstRange.setStart(childNode, leftOffset);
                    return 0;
                }else{
                    leftOffset -= curLen;
                }
            }else
            if (childNode.nodeType === 1) {
                leftOffset = bypassChildNodes(childNode, leftOffset);
            }
        }

        return leftOffset;
    }
}

আমি বর্তমান কেরেটের অবস্থান পাওয়ার জন্য কোডও লিখেছি (পরীক্ষা হয়নি):

//Get offset in current contenteditable field (start offset by default or end offset with calcEnd=true)
function getCurSelectionOffset(calcEnd = false) {
    const sel = window.getSelection();
    if (sel.rangeCount !== 1 || !document.activeElement) return 0;

    const firstRange     = sel.getRangeAt(0),
          startContainer = calcEnd ? firstRange.endContainer : firstRange.startContainer,
          startOffset    = calcEnd ? firstRange.endOffset    : firstRange.startOffset;
    let needStop = false;

    return bypassChildNodes(document.activeElement);



    //Bypass in depth
    function bypassChildNodes(el) {
        const childNodes = el.childNodes;
        let ans = 0;

        if (el === startContainer) {
            if (startContainer.nodeType === 3) {
                ans = startOffset;
            }else
            if (startContainer.nodeType === 1) {
                for (let i = 0; i < startOffset; i++) {
                    const childNode = childNodes[i];

                    ans += childNode.nodeType === 3 ? childNode.textContent.length :
                           childNode.nodeType === 1 ? childNode.innerText.length :
                           0;
                }
            }

            needStop = true;
        }else{
            for (let i = 0; i < childNodes.length && !needStop; i++) {
                const childNode = childNodes[i];
                ans += bypassChildNodes(childNode);
            }
        }

        return ans;
    }
}

আপনার রেঞ্জ.স্টার্টঅফসেট এবং রেঞ্জ সম্পর্কে সচেতন হওয়াও দরকার O range.startContainer এবং range.endContainer গাছের যে কোনও স্তরের যে কোনও উপাদান নোডকে বোঝাতে পারে (অবশ্যই তারা পাঠ্য নোডগুলিও উল্লেখ করতে পারে)।


0

টিম ডাউন এর উত্তরের ভিত্তিতে, তবে এটি সর্বশেষে পরিচিত "ভাল" পাঠ্য সারিটি পরীক্ষা করে। এটি কার্সারটিকে একেবারে শেষে রাখে।

তদুপরি, আমি ডিওএম-এর পরম সর্বশেষ "ভাল" পাঠ্য নোডটি খুঁজতে পুনরুক্তি / পুনরাবৃত্তভাবে প্রতিটি টানা শেষ সন্তানের শেষ সন্তানের চেক করতে পারি।

function onClickHandler() {
  setCaret(document.getElementById("editable"));
}

function setCaret(el) {
  let range = document.createRange(),
      sel = window.getSelection(),
      lastKnownIndex = -1;
  for (let i = 0; i < el.childNodes.length; i++) {
    if (isTextNodeAndContentNoEmpty(el.childNodes[i])) {
      lastKnownIndex = i;
    }
  }
  if (lastKnownIndex === -1) {
    throw new Error('Could not find valid text content');
  }
  let row = el.childNodes[lastKnownIndex],
      col = row.textContent.length;
  range.setStart(row, col);
  range.collapse(true);
  sel.removeAllRanges();
  sel.addRange(range);
  el.focus();
}

function isTextNodeAndContentNoEmpty(node) {
  return node.nodeType == Node.TEXT_NODE && node.textContent.trim().length > 0
}
<div id="editable" contenteditable="true">
  text text text<br>text text text<br>text text text<br>
</div>
<button id="button" onclick="onClickHandler()">focus</button>

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.