বিষয়বস্তু অর্জনযোগ্য ক্যারেট সূচক অবস্থান position


119

আমি কোনও সংখ্যক ভাল, ক্রসব্রোজারকে একটি contentEditableউপাদানটিতে কার্সার বা ক্যারেটের সূচক অবস্থান নির্ধারণের বিষয়ে জবাব দিচ্ছি , তবে কীভাবে এটির সূচক পাওয়া যায় না বা খুঁজে পাওয়া যায় না ...

আমি যা করতে চাই তা হল এই ডিভের মধ্যে ক্যারেটের সূচকটি জানতে keyup

সুতরাং, যখন ব্যবহারকারী পাঠ্যটি টাইপ করছেন, আমি যে কোনও সময়ে contentEditableউপাদানটির মধ্যে এর কার্সার সূচকটি জানতে পারি ।

সম্পাদনা করুন: আমি চাই এর INDEX DIV আছে বিষয়বস্তু (পাঠ্য), না কার্সার স্থানাঙ্ক মধ্যে।

<div id="contentBox" contentEditable="true"></div>

$('#contentbox').keyup(function() { 
    // ... ? 
});

পাঠ্যে এর অবস্থানটি দেখুন। তারপরে, সেই অবস্থানের আগে '@' এর শেষ ঘটনাটি দেখুন। সুতরাং কিছু পাঠ্য যুক্তি।
বার্টওয়ান

এছাড়াও, আমি <diV> এর মধ্যে অন্যান্য ট্যাগগুলিকে অনুমতি দেওয়ার পরিকল্পনা করছি না, কেবল পাঠ্য
বার্টওয়ান

ঠিক আছে, হ্যা আমি করি <p> মধ্যে অন্য ট্যাগগুলি প্রয়োজন যাচ্ছে। এখানে <a> ট্যাগ থাকবে তবে বাসা
বাঁধবে

@ বার্টওয়ান: ক্যারেটটি যদি কোনও <a>উপাদানটির ভিতরে থাকে তবে <div>আপনি কী অফসেটটি চান? ভিতরে টেক্সট মধ্যে অফসেট<a> ?
টিম ডাউন

এটি কখনই কোনও <a> উপাদানটির মধ্যে থাকা উচিত নয়। <a> উপাদানটি এইচটিএমএল হিসাবে রেন্ডার করা উচিত, যাতে ব্যবহারকারী আসলে ক্যারেটটি সেখানে রাখতে পারবেন না।
বার্টওয়ান

উত্তর:


121

নিম্নলিখিত কোড অনুমান:

  • সম্পাদনাযোগ্য <div>এবং অন্য কোনও নোডের মধ্যে সর্বদা একটি পাঠ্য নোড থাকে
  • সম্পাদনাযোগ্য ডিভের সিএসএস white-spaceসম্পত্তি সেট করা নেইpre

যদি আপনার আরও সাধারণ পদ্ধতির প্রয়োজন হয় যা নেস্টেড উপাদানগুলির সাথে সামগ্রীতে কাজ করবে তবে এই উত্তরটি চেষ্টা করে দেখুন:

https://stackoverflow.com/a/4812022/96100

কোড:

function getCaretPosition(editableDiv) {
  var caretPos = 0,
    sel, range;
  if (window.getSelection) {
    sel = window.getSelection();
    if (sel.rangeCount) {
      range = sel.getRangeAt(0);
      if (range.commonAncestorContainer.parentNode == editableDiv) {
        caretPos = range.endOffset;
      }
    }
  } else if (document.selection && document.selection.createRange) {
    range = document.selection.createRange();
    if (range.parentElement() == editableDiv) {
      var tempEl = document.createElement("span");
      editableDiv.insertBefore(tempEl, editableDiv.firstChild);
      var tempRange = range.duplicate();
      tempRange.moveToElementText(tempEl);
      tempRange.setEndPoint("EndToEnd", range);
      caretPos = tempRange.text.length;
    }
  }
  return caretPos;
}
#caretposition {
  font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="contentbox" contenteditable="true">Click me and move cursor with keys or mouse</div>
<div id="caretposition">0</div>
<script>
  var update = function() {
    $('#caretposition').html(getCaretPosition(this));
  };
  $('#contentbox').on("mousedown mouseup keydown keyup", update);
</script>


9
সেখানে অন্য কোনও ট্যাগ থাকলে এটি কাজ করবে না। প্রশ্ন: ক্যারেটটি যদি কোনও <a>উপাদানটির ভিতরে থাকে তবে <div>আপনি কী অফসেটটি চান? লেখার ভিতরে অফসেটটি ভিতরে থাকবে <a>?
টিম ডাউন

3
@ রিচার্ড: ঠিক আছে, এটির keyupজন্য সম্ভবত ভুল ঘটনা হতে পারে তবে এটিই মূল প্রশ্নে ব্যবহৃত হয়েছিল। getCaretPosition()নিজের সীমাবদ্ধতার মধ্যেই এটি ঠিক আছে।
টিম ডাউন

3
যদি আমি এন্টার টিপুন এবং কোনও নতুন লাইনে চলে যাই তবে এই জেএসএফআইডিডিএল ডেমো ব্যর্থ হয়। অবস্থানটি 0.
giorgio79

5
@ জিওরজিও 79৯: হ্যাঁ, কারণ লাইন ব্রেকটি একটি <br>বা <div>উপাদান তৈরি করে, যা উত্তরে বর্ণিত প্রথম অনুমানকে লঙ্ঘন করে। আপনার যদি আরও কিছু সাধারণ সমাধানের প্রয়োজন হয় তবে আপনি স্ট্যাকওভারফ্লো.com
টিম ডাউন

2
এটি লাইন নম্বর অন্তর্ভুক্ত তাই এটি করার জন্য কি এখনও আছে?
আদজিৎ

28

কয়েকটি উত্তেজনার ঝাঁকুনি যা আমি অন্য উত্তরে সম্বোধন করতে দেখছি না:

  1. উপাদানটিতে শিশু নোডের একাধিক স্তর থাকতে পারে (উদাঃ শিশু নোডগুলিতে শিশু নোড রয়েছে এমন শিশু নোড রয়েছে ...)
  2. একটি নির্বাচন বিভিন্ন শুরু এবং শেষ অবস্থান নিয়ে গঠিত হতে পারে (যেমন একাধিক অক্ষর নির্বাচিত হয়)
  3. ক্যারেট শুরু / শেষ সমেত নোড উপাদান বা এর সরাসরি বাচ্চাদের নাও হতে পারে

উপাদানটির পাঠ্য বিষয়বস্তুর মান হিসাবে অফসেট হিসাবে শুরু এবং শেষ পজিশনের একটি উপায় এখানে রয়েছে:

// node_walk: walk the element tree, stop when func(node) returns false
function node_walk(node, func) {
  var result = func(node);
  for(node = node.firstChild; result !== false && node; node = node.nextSibling)
    result = node_walk(node, func);
  return result;
};

// getCaretPosition: return [start, end] as offsets to elem.textContent that
//   correspond to the selected portion of text
//   (if start == end, caret is at given position and no text is selected)
function getCaretPosition(elem) {
  var sel = window.getSelection();
  var cum_length = [0, 0];

  if(sel.anchorNode == elem)
    cum_length = [sel.anchorOffset, sel.extentOffset];
  else {
    var nodes_to_find = [sel.anchorNode, sel.extentNode];
    if(!elem.contains(sel.anchorNode) || !elem.contains(sel.extentNode))
      return undefined;
    else {
      var found = [0,0];
      var i;
      node_walk(elem, function(node) {
        for(i = 0; i < 2; i++) {
          if(node == nodes_to_find[i]) {
            found[i] = true;
            if(found[i == 0 ? 1 : 0])
              return false; // all done
          }
        }

        if(node.textContent && !node.firstChild) {
          for(i = 0; i < 2; i++) {
            if(!found[i])
              cum_length[i] += node.textContent.length;
          }
        }
      });
      cum_length[0] += sel.anchorOffset;
      cum_length[1] += sel.extentOffset;
    }
  }
  if(cum_length[0] <= cum_length[1])
    return cum_length;
  return [cum_length[1], cum_length[0]];
}

3
এটি অবশ্যই সঠিক উত্তর হিসাবে নির্বাচন করা উচিত। এটি পাঠ্যের ভিতরে ট্যাগগুলির সাথে কাজ করে (স্বীকৃত প্রতিক্রিয়া হয় না)
hamboy75

17

$("#editable").on('keydown keyup mousedown mouseup',function(e){
		   
       if($(window.getSelection().anchorNode).is($(this))){
    	  $('#position').html('0')
       }else{
         $('#position').html(window.getSelection().anchorOffset);
       }
 });
body{
  padding:40px;
}
#editable{
  height:50px;
  width:400px;
  border:1px solid #000;
}
#editable p{
  margin:0;
  padding:0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<div contenteditable="true" id="editable">move the cursor to see position</div>
<div>
position : <span id="position"></span>
</div>


3
দুর্ভাগ্যক্রমে আপনি প্রবেশের সাথে সাথে অন্য লাইনে শুরু করার সাথে সাথে কাজ করা বন্ধ করে দেয় (এটি আবার 0 থেকে শুরু হয় - সম্ভবত সিআর / এলএফ থেকে গণনা করা হচ্ছে)।
ইয়ান

আপনার কিছু বোল্ড এবং / অথবা ইটালিক শব্দ থাকলে এটি সঠিকভাবে কাজ করে না।
ব্যবহারকারী 2824371

14

এটা চেষ্টা কর:

Caret.js ক্যারেট পোস্ট করুন এবং পাঠ্য ক্ষেত্র থেকে অফসেট পান

https://github.com/ichord/Caret.js

ডেমো: http://ichord.github.com/Care.js


এটা মিষ্টি. আমার কন্টেন্টটির contenteditable liপুনর্নবীকরণের জন্য বোতামে ক্লিক করার পরে ক্যারেট সেট করতে আমার এই আচরণের প্রয়োজন ছিল li
আকিনুরি

@ অ্যান্ড্রয়েডদেভ আমি Caret.js এর লেখক নই তবে আপনি কি বিবেচনা করেছেন যে সমস্ত প্রধান ব্রাউজারের জন্য ক্যারেটের অবস্থানটি কয়েকটি লাইনের চেয়ে জটিল? আপনি কি জানেন বা একটি অ-সজ্জিত বিকল্প তৈরি করেছেন যা আপনি আমাদের সাথে ভাগ করতে পারেন?
adelriosantiago

8

কিন্ডা দেরিতে দেরীতে, তবে অন্য কারও পক্ষে লড়াই করতে চাইলে। গত দু'দিন ধরে আমি যে গুগল সন্ধান পেয়েছি তার কোনওটিই কাজ করে না এমন কিছু নিয়ে আসে নি, তবে আমি একটি সংক্ষিপ্ত এবং মার্জিত সমাধান নিয়ে এসেছি যা আপনার যত নেস্টেড ট্যাগ থাকুক না কেন সর্বদা কাজ করবে:

function cursor_position() {
    var sel = document.getSelection();
    sel.modify("extend", "backward", "paragraphboundary");
    var pos = sel.toString().length;
    if(sel.anchorNode != undefined) sel.collapseToEnd();

    return pos;
}

// Demo:
var elm = document.querySelector('[contenteditable]');
elm.addEventListener('click', printCaretPosition)
elm.addEventListener('keydown', printCaretPosition)

function printCaretPosition(){
  console.log( cursor_position(), 'length:', this.textContent.trim().length )
}
<div contenteditable>some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text</div>

এটি অনুচ্ছেদের শুরুতে সমস্ত পথ নির্বাচন করে এবং তারপরে বর্তমান অবস্থানটি পেতে স্ট্রিংয়ের দৈর্ঘ্য গণনা করে এবং কার্সারটিকে বর্তমান অবস্থানে ফিরিয়ে আনার জন্য নির্বাচনটিকে পূর্বাবস্থায় ফিরিয়ে দেয়। আপনি যদি একটি সম্পূর্ণ ডকুমেন্টের জন্য এটি করতে চান (একাধিক অনুচ্ছেদে), তারপরে পরিবর্তন paragraphboundaryকরুন documentboundaryবা আপনার মামলার যা কিছু গ্রানুলারিটি। আরও তথ্যের জন্য API টি দেখুন । চিয়ার্স! :)


1
যদি আমার কাছে <div contenteditable> some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text </div> এভারটাইম আমি iট্যাগের আগে কার্সার রাখি বা ভিতরে কোনও শিশু এইচটিএমএল উপাদান div, কার্সার অবস্থানটি 0 থেকে শুরু হয় তবে এই পুনরায় আরম্ভের গণনা থেকে বাঁচার কোনও উপায় আছে কি?
ভাম

অস্বাভাবিক. আমি ক্রোমে সেই আচরণ পাচ্ছি না। আপনি কোন ব্রাউজার ব্যবহার করছেন?
Soubriquet

2
দেখে মনে হচ্ছে নির্বাচন.মোডিফাই করা সমস্ত ব্রাউজারে সমর্থিত বা নাও থাকতে পারে। বিকাশকারী.মোজিলা.আর.ইন-
ক্রিস সুলিভান

7
function getCaretPosition() {
    var x = 0;
    var y = 0;
    var sel = window.getSelection();
    if(sel.rangeCount) {
        var range = sel.getRangeAt(0).cloneRange();
        if(range.getClientRects()) {
        range.collapse(true);
        var rect = range.getClientRects()[0];
        if(rect) {
            y = rect.top;
            x = rect.left;
        }
        }
    }
    return {
        x: x,
        y: y
    };
}

এইটি আমার জন্য আসলে কাজ করেছিল, আমি উপরের সমস্তগুলি চেষ্টা করেছি, তারা করেনি।
iStudLion

আপনাকে ধন্যবাদ তবে এটি নতুন লাইনে {x: 0, y: 0 return প্রদান করে।
হিচামকাজান

এটি পিক্সেল অবস্থানটি দেয়, অক্ষর অফসেট নয়
4esn0k

ধন্যবাদ, আমি ক্যারেট থেকে পিক্সেল অবস্থান পুনরুদ্ধার করতে খুঁজছিলাম এবং এটি ভাল কাজ করছে।
সমীশ

6

উইন্ডো.জেট নির্বাচন - বনাম - ডকুমেন্ট.স্লেশন lection

এটি আমার জন্য কাজ করে:

function getCaretCharOffset(element) {
  var caretOffset = 0;

  if (window.getSelection) {
    var range = window.getSelection().getRangeAt(0);
    var preCaretRange = range.cloneRange();
    preCaretRange.selectNodeContents(element);
    preCaretRange.setEnd(range.endContainer, range.endOffset);
    caretOffset = preCaretRange.toString().length;
  } 

  else if (document.selection && document.selection.type != "Control") {
    var textRange = document.selection.createRange();
    var preCaretTextRange = document.body.createTextRange();
    preCaretTextRange.moveToElementText(element);
    preCaretTextRange.setEndPoint("EndToEnd", textRange);
    caretOffset = preCaretTextRange.text.length;
  }

  return caretOffset;
}


// Demo:
var elm = document.querySelector('[contenteditable]');
elm.addEventListener('click', printCaretPosition)
elm.addEventListener('keydown', printCaretPosition)

function printCaretPosition(){
  console.log( getCaretCharOffset(elm), 'length:', this.textContent.trim().length )
}
<div contenteditable>some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text</div>

কলিং লাইন ইভেন্টের ধরণের উপর নির্ভর করে, মূল ইভেন্টের জন্য এটি ব্যবহার করুন:

getCaretCharOffsetInDiv(e.target) + ($(window.getSelection().getRangeAt(0).startContainer.parentNode).index());

মাউস ইভেন্টের জন্য এটি ব্যবহার করুন:

getCaretCharOffsetInDiv(e.target.parentElement) + ($(e.target).index())

এই দুটি ক্ষেত্রে আমি লক্ষ্য সূচকটি যুক্ত করে ব্রেক লাইনগুলির জন্য যত্ন নিই


4
//global savedrange variable to store text range in
var savedrange = null;

function getSelection()
{
    var savedRange;
    if(window.getSelection && window.getSelection().rangeCount > 0) //FF,Chrome,Opera,Safari,IE9+
    {
        savedRange = window.getSelection().getRangeAt(0).cloneRange();
    }
    else if(document.selection)//IE 8 and lower
    { 
        savedRange = document.selection.createRange();
    }
    return savedRange;
}

$('#contentbox').keyup(function() { 
    var currentRange = getSelection();
    if(window.getSelection)
    {
        //do stuff with standards based object
    }
    else if(document.selection)
    { 
        //do stuff with microsoft object (ie8 and lower)
    }
});

দ্রষ্টব্য: পরিসীমা অবজেক্ট এর স্ব স্ব চলকটিতে সংরক্ষণ করা যেতে পারে এবং সন্তোষজনক ডিভ পরিবর্তন না করে যেকোন সময় পুনরায় নির্বাচন করা যেতে পারে।

আইই 8 এবং নিম্নের জন্য রেফারেন্স: http://msdn.microsoft.com/en-us/library/ms535872(VS.85).aspx

স্ট্যান্ডার্ডগুলি (অন্যান্য সমস্ত) ব্রাউজারগুলির জন্য রেফারেন্স: https://developer.mozilla.org/en/DOM/range (এটির মজিলা ডক্স, তবে কোড ক্রোম, সাফারি, অপেরা এবং আই 9 তেও কাজ করে)


1
ধন্যবাদ, তবে ডিভ বিষয়বস্তুগুলিতে কেরেটের অবস্থানের 'সূচক' ঠিক কীভাবে পাব?
বার্টওয়ান

ঠিক আছে, দেখে মনে হচ্ছে .baseOffset কে .getSelection () এ কল করে। সুতরাং এটি আপনার উত্তরের সাথে আমার প্রশ্নের উত্তর দেয়। ধন্যবাদ!
বার্টওয়ান

2
দুর্ভাগ্যবশত .baseOffset কেবল ওয়েবকিটে কাজ করে (আমি মনে করি)। এটি কেবল আপনাকে ক্যারেটের অবিস্মৃত পিতামাতার অফসেটটি দেয় (যদি আপনার <div> ভিতরে <b> ট্যাগ থাকে তবে এটি <b> শুরু থেকে অফসেটটি দেবে, <div> এর শুরু নয়) স্ট্যান্ডার্ডভিত্তিক রেঞ্জগুলি নির্বাচনের প্যারেন্ট নোড থেকে অফসেট পেতে রেঞ্জ.এন্ডঅফসেট রেঞ্জ.স্টার্টঅফসেট রেঞ্জ.এন্ডকন্টেনার এবং রেঞ্জ.স্টার্টকন্টেইনার ব্যবহার করতে পারে এবং নোড নিজেই (এতে টেক্সট নোডস অন্তর্ভুক্ত রয়েছে)। বাম দিক থেকে পিক্সেল থেকে অফসেট , এবং তাই অকেজো।
নিকো বার্নস

কেবলমাত্র পরিসীমাটির নিজের জিনিসটি সংরক্ষণ করা এবং উইন্ডো.সেটলেশন () ব্যবহার করা ভাল is <- স্ট্যান্ডার্ড এবং রেঞ্জ। সিলেক্ট (); <- একই জায়গায় কার্সারটিকে পুনরায় অবস্থানের জন্য IE। range.insertNode (nodetoinsert); <- স্ট্যান্ডার্ড এবং রেঞ্জ.পাস্টএইচটিএমএল (এইচটিএমএল কোড); <- আইটি কার্সারে পাঠ্য বা এইচটিএমএল সন্নিবেশ করানোর জন্য।
নিকো বার্নস

Rangeবস্তুর সবচেয়ে ব্রাউজার এবং দ্বারা ফিরে TextRangeবস্তুর ইন্টারনেট দ্বারা ফিরে, অত্যন্ত ভিন্ন জিনিস তাই আমি নিশ্চিত এই উত্তরটি সমাধান অনেক নই।
টিম ডাউন

3

যেহেতু এটি আমাকে নতুন উইন্ডোটি ব্যবহার করে চিরকালের জন্য নিয়ে গেছে getgetSelection এপিআই আমি উত্তরোত্তর ভাগ করতে যাচ্ছি। নোট করুন যে MDN পরামর্শ দেয় উইন্ডো.জেট-সিলেকশনের জন্য আরও বিস্তৃত সমর্থন রয়েছে, তবে, আপনার মাইলেজটি ভিন্ন হতে পারে।

const getSelectionCaretAndLine = () => {
    // our editable div
    const editable = document.getElementById('editable');

    // collapse selection to end
    window.getSelection().collapseToEnd();

    const sel = window.getSelection();
    const range = sel.getRangeAt(0);

    // get anchor node if startContainer parent is editable
    let selectedNode = editable === range.startContainer.parentNode
      ? sel.anchorNode 
      : range.startContainer.parentNode;

    if (!selectedNode) {
        return {
            caret: -1,
            line: -1,
        };
    }

    // select to top of editable
    range.setStart(editable.firstChild, 0);

    // do not use 'this' sel anymore since the selection has changed
    const content = window.getSelection().toString();
    const text = JSON.stringify(content);
    const lines = (text.match(/\\n/g) || []).length + 1;

    // clear selection
    window.getSelection().collapseToEnd();

    // minus 2 because of strange text formatting
    return {
        caret: text.length - 2, 
        line: lines,
    }
} 

এখানে একটি জিসফিল যা কীআপে আগুন দেয়। তবে নোট করুন, সেই দ্রুত দিকনির্দেশক কী টিপগুলি এবং দ্রুত মুছে ফেলা ঘটনাগুলি এড়িয়ে যাওয়া বলে মনে হচ্ছে।


আমার জন্য কাজ কর! তোমাকে অনেক ধন্যবাদ.
dmodo

এই পাঠ্যটি নির্বাচনটি ধসে পড়ার ফলে আর সম্ভব নয়। সম্ভাব্য পরিস্থিতি: প্রতিটি কীআপ ইভেন্টের মূল্যায়ন করা দরকার
hschmieder

0

একটি সোজা সামনের দিকে, এটি কনটেন্টেবল ডিভের সমস্ত চিদরেন দিয়ে পুনরাবৃত্তি করে যতক্ষণ না এটি এন্ডকন্টেইনারটিকে আঘাত করে। তারপরে আমি শেষ পাত্রে অফসেট যুক্ত করব এবং আমাদের কাছে অক্ষর সূচক রয়েছে। যে কোনও সংখ্যা বাসা নিয়ে কাজ করা উচিত। পুনরাবৃত্তি ব্যবহার করে।

দ্রষ্টব্য: সমর্থন করার জন্য একটি পলি ফিল প্রয়োজনElement.closest('div[contenteditable]')

https://codepen.io/alockwood05/pen/vMpdmZ

function caretPositionIndex() {
    const range = window.getSelection().getRangeAt(0);
    const { endContainer, endOffset } = range;

    // get contenteditableDiv from our endContainer node
    let contenteditableDiv;
    const contenteditableSelector = "div[contenteditable]";
    switch (endContainer.nodeType) {
      case Node.TEXT_NODE:
        contenteditableDiv = endContainer.parentElement.closest(contenteditableSelector);
        break;
      case Node.ELEMENT_NODE:
        contenteditableDiv = endContainer.closest(contenteditableSelector);
        break;
    }
    if (!contenteditableDiv) return '';


    const countBeforeEnd = countUntilEndContainer(contenteditableDiv, endContainer);
    if (countBeforeEnd.error ) return null;
    return countBeforeEnd.count + endOffset;

    function countUntilEndContainer(parent, endNode, countingState = {count: 0}) {
      for (let node of parent.childNodes) {
        if (countingState.done) break;
        if (node === endNode) {
          countingState.done = true;
          return countingState;
        }
        if (node.nodeType === Node.TEXT_NODE) {
          countingState.count += node.length;
        } else if (node.nodeType === Node.ELEMENT_NODE) {
          countUntilEndContainer(node, endNode, countingState);
        } else {
          countingState.error = true;
        }
      }
      return countingState;
    }
  }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.