programing

contentEditable에 contentEditable에 contentEditable에

easyjava 2023. 9. 6. 23:07
반응형

contentEditable에

contentEditable='on' <div>가 포커스를 되찾았을 때 커서/키보드 위치를 마지막으로 알려진 위치로 설정하는 최종 교차 검색 솔루션을 찾고 있습니다.클릭할 때마다 캐럿/커서를 디브의 텍스트 맨 앞으로 이동하는 내용 편집 가능한 디브의 기본 기능으로 나타납니다. 이는 바람직하지 않습니다.

디바의 포커스를 벗어날 때 현재 커서 위치를 변수에 저장했다가 다시 포커스가 안에 있을 때 다시 설정해야 할 것 같은데 아직 합치지 못했고 작동 코드 샘플을 찾지 못했습니다.

생각이 있으신 분, 작업 코드 조각 또는 샘플이 있으시면 기꺼이 만나겠습니다.

아직 코드는 없지만, 여기 제가 가지고 있는 것이 있습니다.

<script type="text/javascript">
// jQuery
$(document).ready(function() {
   $('#area').focus(function() { .. }  // focus I would imagine I need.
}
</script>
<div id="area" contentEditable="true"></div>

PS. 이 리소스를 사용해 보았지만 <div>에서 작동하지 않는 것 같습니다.텍스트 영역에 대해서만 가능합니다(Content Editable 엔티티 끝으로 커서를 이동하는 방법)

이 솔루션은 모든 주요 브라우저에서 작동합니다.

saveSelection()어다에 되어 있습니다.onmouseup그리고.onkeyup di의 events 건)다에택을를다을택고s에ee(s .savedRange.

restoreSelection()어다에 되어 있습니다.onfocus의고을시다된다시n를에 저장된 선택을 다시 선택합니다.savedRange.

사용자가 div를 클릭할 때도 선택 항목이 복원되도록 하지 않는 한 완벽하게 작동합니다(이는 일반적으로 커서가 클릭한 위치로 이동할 것으로 예상되지만 완성도를 위해 코드가 포함되어 있기 때문에 다소 직관적이지 않습니다).

는 하기 를 .onclick그리고.onmousedown는에해다en에 의해 취소됩니다.cancelEvent()이벤트를 취소하기 위한 크로스 브라우저 기능입니다.cancelEvent()는 또한는한다합니다.restoreSelection()함수는 클릭 이벤트가 취소될 때 디브가 포커스를 받지 않으므로 이 함수가 실행되지 않는 한 아무것도 선택되지 않습니다.

isInFocus 을 화"하는지 로"를 합니다"고 화"" 로"로 변경되는지 여부를 저장합니다.onblur고 "true"onfocus할 수 선택 항목을 할 수 없습니다 그러면 div가 포커스에 있지 않은 경우에만 클릭 이벤트를 취소할 수 있습니다(그렇지 않으면 선택 항목을 전혀 변경할 수 없습니다).

디바가 클릭으로 포커스가 맞춰질 때 선택 항목을 변경하고 선택 항목을 복원하지 않으려면onclick으로 o)를 사용하여 에 포커스를 할 경우에만 해당)document.getElementById("area").focus(); 유사한 는한히다음ee히는r한yr음nonclick그리고.onmousedown사건들. 더onblur과.onDivBlur()그리고.cancelEvent()또한 이러한 상황에서 안전하게 기능을 제거할 수 있습니다.

이 코드는 html 페이지의 본문에 직접 드롭된 경우에 사용할 수 있습니다.

<div id="area" style="width:300px;height:300px;" onblur="onDivBlur();" onmousedown="return cancelEvent(event);" onclick="return cancelEvent(event);" contentEditable="true" onmouseup="saveSelection();" onkeyup="saveSelection();" onfocus="restoreSelection();"></div>
<script type="text/javascript">
var savedRange,isInFocus;
function saveSelection()
{
    if(window.getSelection)//non IE Browsers
    {
        savedRange = window.getSelection().getRangeAt(0);
    }
    else if(document.selection)//IE
    { 
        savedRange = document.selection.createRange();  
    } 
}

function restoreSelection()
{
    isInFocus = true;
    document.getElementById("area").focus();
    if (savedRange != null) {
        if (window.getSelection)//non IE and there is already a selection
        {
            var s = window.getSelection();
            if (s.rangeCount > 0) 
                s.removeAllRanges();
            s.addRange(savedRange);
        }
        else if (document.createRange)//non IE and no selection
        {
            window.getSelection().addRange(savedRange);
        }
        else if (document.selection)//IE
        {
            savedRange.select();
        }
    }
}
//this part onwards is only needed if you want to restore selection onclick
var isInFocus = false;
function onDivBlur()
{
    isInFocus = false;
}

function cancelEvent(e)
{
    if (isInFocus == false && savedRange != null) {
        if (e && e.preventDefault) {
            //alert("FF");
            e.stopPropagation(); // DOM style (return false doesn't always work in FF)
            e.preventDefault();
        }
        else {
            window.event.cancelBubble = true;//IE stopPropagation
        }
        restoreSelection();
        return false; // false = IE style
    }
}
</script>

이것은 표준 기반 브라우저와 호환되지만 IE에서는 아마도 실패할 것입니다.출발점으로 제공하는 겁니다.IE는 DOM 범위를 지원하지 않습니다.

var editable = document.getElementById('editable'),
    selection, range;

// Populates selection and range variables
var captureSelection = function(e) {
    // Don't capture selection outside editable region
    var isOrContainsAnchor = false,
        isOrContainsFocus = false,
        sel = window.getSelection(),
        parentAnchor = sel.anchorNode,
        parentFocus = sel.focusNode;

    while(parentAnchor && parentAnchor != document.documentElement) {
        if(parentAnchor == editable) {
            isOrContainsAnchor = true;
        }
        parentAnchor = parentAnchor.parentNode;
    }

    while(parentFocus && parentFocus != document.documentElement) {
        if(parentFocus == editable) {
            isOrContainsFocus = true;
        }
        parentFocus = parentFocus.parentNode;
    }

    if(!isOrContainsAnchor || !isOrContainsFocus) {
        return;
    }

    selection = window.getSelection();

    // Get range (standards)
    if(selection.getRangeAt !== undefined) {
        range = selection.getRangeAt(0);

    // Get range (Safari 2)
    } else if(
        document.createRange &&
        selection.anchorNode &&
        selection.anchorOffset &&
        selection.focusNode &&
        selection.focusOffset
    ) {
        range = document.createRange();
        range.setStart(selection.anchorNode, selection.anchorOffset);
        range.setEnd(selection.focusNode, selection.focusOffset);
    } else {
        // Failure here, not handled by the rest of the script.
        // Probably IE or some older browser
    }
};

// Recalculate selection while typing
editable.onkeyup = captureSelection;

// Recalculate selection after clicking/drag-selecting
editable.onmousedown = function(e) {
    editable.className = editable.className + ' selecting';
};
document.onmouseup = function(e) {
    if(editable.className.match(/\sselecting(\s|$)/)) {
        editable.className = editable.className.replace(/ selecting(\s|$)/, '');
        captureSelection();
    }
};

editable.onblur = function(e) {
    var cursorStart = document.createElement('span'),
        collapsed = !!range.collapsed;

    cursorStart.id = 'cursorStart';
    cursorStart.appendChild(document.createTextNode('—'));

    // Insert beginning cursor marker
    range.insertNode(cursorStart);

    // Insert end cursor marker if any text is selected
    if(!collapsed) {
        var cursorEnd = document.createElement('span');
        cursorEnd.id = 'cursorEnd';
        range.collapse();
        range.insertNode(cursorEnd);
    }
};

// Add callbacks to afterFocus to be called after cursor is replaced
// if you like, this would be useful for styling buttons and so on
var afterFocus = [];
editable.onfocus = function(e) {
    // Slight delay will avoid the initial selection
    // (at start or of contents depending on browser) being mistaken
    setTimeout(function() {
        var cursorStart = document.getElementById('cursorStart'),
            cursorEnd = document.getElementById('cursorEnd');

        // Don't do anything if user is creating a new selection
        if(editable.className.match(/\sselecting(\s|$)/)) {
            if(cursorStart) {
                cursorStart.parentNode.removeChild(cursorStart);
            }
            if(cursorEnd) {
                cursorEnd.parentNode.removeChild(cursorEnd);
            }
        } else if(cursorStart) {
            captureSelection();
            var range = document.createRange();

            if(cursorEnd) {
                range.setStartAfter(cursorStart);
                range.setEndBefore(cursorEnd);

                // Delete cursor markers
                cursorStart.parentNode.removeChild(cursorStart);
                cursorEnd.parentNode.removeChild(cursorEnd);

                // Select range
                selection.removeAllRanges();
                selection.addRange(range);
            } else {
                range.selectNode(cursorStart);

                // Select range
                selection.removeAllRanges();
                selection.addRange(range);

                // Delete cursor marker
                document.execCommand('delete', false, null);
            }
        }

        // Call callbacks here
        for(var i = 0; i < afterFocus.length; i++) {
            afterFocus[i]();
        }
        afterFocus = [];

        // Register selection again
        captureSelection();
    }, 10);
};

갱신하다

아래에 게시한 코드의 개선된 버전을 통합한 Rangy라는 크로스 브라우저 범위 및 선택 라이브러리를 작성했습니다.이 특정 질문에 선택 저장복원 모듈을 사용할 수 있지만, 프로젝트에서 선택 항목에 대해 다른 작업을 수행하지 않고 많은 라이브러리가 필요 없다면 @Nico Burns의 답변과 같은 것을 사용하고 싶습니다.

기답

IERANGE 사용가능 (http://code.google.com/p/ierange/)IE의 TextRange를 DOM Range와 같은 것으로 변환하여 눈꺼풀 없는 시작점과 같은 것과 함께 사용합니다.개인적으로 저는 전체를 사용하기 보다는 Range <-> TextRange 변환을 수행하는 IERange의 알고리즘만 사용할 것입니다.그리고 IE의 선택 개체에는 포커스 노드와 앵커 노드 속성이 없으며 대신 선택에서 얻은 범위/텍스트 범위를 사용할 수 있습니다.

이것을 하기 위해 무언가를 만들 수도 있고, 만약 그렇게 한다면 여기에 다시 게시할 것입니다.

편집:

이것을 하는 스크립트의 데모를 만들었습니다.아직 알아보지 못한 오페라 9의 버그를 제외하고는 지금까지 시도해 본 모든 것에서 작동합니다.윈도에서 작동하는 브라우저는 IE 5.5, 6, 7, 크롬 2, 파이어폭스 2, 3, 3.5, 사파리 4 등입니다.

http://www.timdown.co.uk/code/selections/

포커스 노드가 선택 시작에 있도록 브라우저에서 거꾸로 선택할 수 있으며 오른쪽 또는 왼쪽 커서 키를 누르면 캐럿이 선택 시작에 대한 상대 위치로 이동됩니다.선택을 복원할 때 이를 복제하는 것은 불가능하다고 생각하기 때문에 포커스 노드는 항상 선택의 끝에 있습니다.

저는 조만간 이것을 완전히 작성할 것입니다.

관련 상황이 있었는데, 특히 커서 위치를 컨텐츠 편집 가능한 디브의 END로 설정해야 했습니다.저는 Rangy처럼 본격적인 라이브러리를 사용하고 싶지 않았고, 많은 솔루션이 너무 무거웠습니다.

결국 저는 콘텐츠 편집 디브의 끝에 캐럿 위치를 설정하는 간단한 jQuery 함수를 고안했습니다.

$.fn.focusEnd = function() {
    $(this).focus();
    var tmp = $('<span />').appendTo($(this)),
        node = tmp.get(0),
        range = null,
        sel = null;

    if (document.selection) {
        range = document.body.createTextRange();
        range.moveToElementText(node);
        range.select();
    } else if (window.getSelection) {
        range = document.createRange();
        range.selectNode(node);
        sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    }
    tmp.remove();
    return this;
}

이론은 간단합니다: 편집 가능한 것의 끝에 스판을 추가하고 선택한 다음 스판을 제거하면 디브의 끝에 커서가 남게 됩니다.원하는 곳에 스팬을 삽입하도록 이 솔루션을 적용하여 커서를 특정 지점에 놓을 수 있습니다.

사용 방법은 간단합니다.

$('#editable').focusEnd();

그거에요!

Nico Burns의 답변을 받아 jQuery를 사용해 만들었습니다.

  • 일반:에 대하여div contentEditable="true"
  • 더 짧은

jQuery 1.6 이상이 필요합니다.

savedRanges = new Object();
$('div[contenteditable="true"]').focus(function(){
    var s = window.getSelection();
    var t = $('div[contenteditable="true"]').index(this);
    if (typeof(savedRanges[t]) === "undefined"){
        savedRanges[t]= new Range();
    } else if(s.rangeCount > 0) {
        s.removeAllRanges();
        s.addRange(savedRanges[t]);
    }
}).bind("mouseup keyup",function(){
    var t = $('div[contenteditable="true"]').index(this);
    savedRanges[t] = window.getSelection().getRangeAt(0);
}).on("mousedown click",function(e){
    if(!$(this).is(":focus")){
        e.stopPropagation();
        e.preventDefault();
        $(this).focus();
    }
});

savedRanges = new Object();
$('div[contenteditable="true"]').focus(function(){
    var s = window.getSelection();
    var t = $('div[contenteditable="true"]').index(this);
    if (typeof(savedRanges[t]) === "undefined"){
        savedRanges[t]= new Range();
    } else if(s.rangeCount > 0) {
        s.removeAllRanges();
        s.addRange(savedRanges[t]);
    }
}).bind("mouseup keyup",function(){
    var t = $('div[contenteditable="true"]').index(this);
    savedRanges[t] = window.getSelection().getRangeAt(0);
}).on("mousedown click",function(e){
    if(!$(this).is(":focus")){
        e.stopPropagation();
        e.preventDefault();
        $(this).focus();
    }
});
div[contenteditable] {
    padding: 1em;
    font-family: Arial;
    outline: 1px solid rgba(0,0,0,0.5);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contentEditable="true"></div>
<div contentEditable="true"></div>
<div contentEditable="true"></div>

장난을 친 후 위의 눈꺼풀 없는 답변을 수정하여 jQuery 플러그인으로 만들어서 다음 중 하나만 수행할 수 있습니다.

var html = "The quick brown fox";
$div.html(html);

// Select at the text "quick":
$div.setContentEditableSelection(4, 5);

// Select at the beginning of the contenteditable div:
$div.setContentEditableSelection(0);

// Select at the end of the contenteditable div:
$div.setContentEditableSelection(html.length);

긴 코드 게시물은 죄송하지만, 누군가에게 도움이 될 수도 있습니다.

$.fn.setContentEditableSelection = function(position, length) {
    if (typeof(length) == "undefined") {
        length = 0;
    }

    return this.each(function() {
        var $this = $(this);
        var editable = this;
        var selection;
        var range;

        var html = $this.html();
        html = html.substring(0, position) +
            '<a id="cursorStart"></a>' +
            html.substring(position, position + length) +
            '<a id="cursorEnd"></a>' +
            html.substring(position + length, html.length);
        console.log(html);
        $this.html(html);

        // Populates selection and range variables
        var captureSelection = function(e) {
            // Don't capture selection outside editable region
            var isOrContainsAnchor = false,
                isOrContainsFocus = false,
                sel = window.getSelection(),
                parentAnchor = sel.anchorNode,
                parentFocus = sel.focusNode;

            while (parentAnchor && parentAnchor != document.documentElement) {
                if (parentAnchor == editable) {
                    isOrContainsAnchor = true;
                }
                parentAnchor = parentAnchor.parentNode;
            }

            while (parentFocus && parentFocus != document.documentElement) {
                if (parentFocus == editable) {
                    isOrContainsFocus = true;
                }
                parentFocus = parentFocus.parentNode;
            }

            if (!isOrContainsAnchor || !isOrContainsFocus) {
                return;
            }

            selection = window.getSelection();

            // Get range (standards)
            if (selection.getRangeAt !== undefined) {
                range = selection.getRangeAt(0);

                // Get range (Safari 2)
            } else if (
                document.createRange &&
                selection.anchorNode &&
                selection.anchorOffset &&
                selection.focusNode &&
                selection.focusOffset
            ) {
                range = document.createRange();
                range.setStart(selection.anchorNode, selection.anchorOffset);
                range.setEnd(selection.focusNode, selection.focusOffset);
            } else {
                // Failure here, not handled by the rest of the script.
                // Probably IE or some older browser
            }
        };

        // Slight delay will avoid the initial selection
        // (at start or of contents depending on browser) being mistaken
        setTimeout(function() {
            var cursorStart = document.getElementById('cursorStart');
            var cursorEnd = document.getElementById('cursorEnd');

            // Don't do anything if user is creating a new selection
            if (editable.className.match(/\sselecting(\s|$)/)) {
                if (cursorStart) {
                    cursorStart.parentNode.removeChild(cursorStart);
                }
                if (cursorEnd) {
                    cursorEnd.parentNode.removeChild(cursorEnd);
                }
            } else if (cursorStart) {
                captureSelection();
                range = document.createRange();

                if (cursorEnd) {
                    range.setStartAfter(cursorStart);
                    range.setEndBefore(cursorEnd);

                    // Delete cursor markers
                    cursorStart.parentNode.removeChild(cursorStart);
                    cursorEnd.parentNode.removeChild(cursorEnd);

                    // Select range
                    selection.removeAllRanges();
                    selection.addRange(range);
                } else {
                    range.selectNode(cursorStart);

                    // Select range
                    selection.removeAllRanges();
                    selection.addRange(range);

                    // Delete cursor marker
                    document.execCommand('delete', false, null);
                }
            }

            // Register selection again
            captureSelection();
        }, 10);
    });
};

최신 브라우저에서 지원하는 selectNodeContents를 활용할 수 있습니다.

var el = document.getElementById('idOfYoursContentEditable');
var selection = window.getSelection();
var range = document.createRange();
selection.removeAllRanges();
range.selectNodeContents(el);
range.collapse(false);
selection.addRange(range);
el.focus();

Firefox에서는 하위 노드에서 div의 텍스트를 사용할 수 있습니다(o_div.childNodes[0])

var range = document.createRange();

range.setStart(o_div.childNodes[0],last_caret_pos);
range.setEnd(o_div.childNodes[0],last_caret_pos);
range.collapse(false);

var sel = window.getSelection(); 
sel.removeAllRanges();
sel.addRange(range);

파티에 늦었을 수도 있지만, 프론트엔드의 돔만 조작하는 대신 편집 중인 태그의 현재 값이 돔에 렌더링되는 값이 아닌 변수에 저장할 수 있습니다.그러면 페이지 새로 고침을 감지하고 내부 값을 변경할 수 있습니다.태그의 HTML을 사용하거나, 제 경우에는 변경 사항을 추적하여 백엔드로 전송하면 됩니다.아마 제 말은 아무런 정의를 내리지 못할 것이기 때문에 저는 제 사용 사례에 적합한 코드 예시를 작성하겠습니다.

import React, { ChangeEvent, useEffect, useState } from "react";

interface IParentProps {}

const ParentComp: React.FC<IParentProps> = (props) => {
  const [innerValue, setInnerValue] = useState<string>();
  const [ghostValue, setGhostValue] = useState<string>();
  // create some boolean to detect when the enter key was pressed in the input field so that you
  //can remove the input field and add the child component
  const handleChange = (event: ChangeEvent<HTMLDivElement>) => {
    setInnerValue(event.currentTarget.innerHTML);
    setGhostValue(event.currentTarget.innerHTML);
  };

  const handleGhostChange = (event: ChangeEvent<HTMLDivElement>) => {
    setGhostValue(event.currentTarget.innerHTML);
  };

  //handle screen refresh, or send the ghost value to the backend
  useEffect(() => {}, []);

  return (
    <div>
      <input type="text" onChange={handleChange} />

      <ChildComponent handleChange={handleGhostChange}>
        {innerValue}
      </ChildComponent>
    </div>
  );
};

interface IChildProps {
  handleChange: (e: ChangeEvent<HTMLDivElement>) => void;
  children: React.ReactNode;
}

const ChildComponent: React.FC<IChildProps> = (props) => {
  return (
    <p
      contentEditable="true"
      suppressContentEditableWarning={true}
      onInput={props.handleChange}
    >
      {props.children}
    </p>
  );
};

만약 제가 기꺼이 그리고 능력이 있는 타자기 붓기 없이 제가 답변을 수정하기를 바란다면 이것이 의미가 있기를 바랍니다.만약 이것이 당신들에게 효과가 있다면 제가 이것이 훨씬 더 간단한 해결책이라고 생각한다는 것을 알려주세요. 그러면 당신이 개인적으로 원하는 방식으로 커서를 재구성하려고 시도합니다.

언급URL : https://stackoverflow.com/questions/1181700/set-cursor-position-on-contenteditable-div

반응형