[javascript] 붙여 넣기 이벤트시 JavaScript에서 클립 보드 데이터 가져 오기 (크로스 브라우저)

웹 애플리케이션이 붙여 넣기 이벤트를 감지하고 붙여 넣을 데이터를 검색하는 방법은 무엇입니까?

텍스트를 서식있는 텍스트 편집기에 붙여 넣기 전에 HTML 내용을 제거하고 싶습니다.

이후에 붙여 넣은 후 텍스트를 정리하면 문제가 해결되지만 이전의 모든 서식이 손실됩니다. 예를 들어 편집기에서 문장을 작성하고 굵게 표시 할 수 있지만 새 텍스트를 붙여 넣으면 모든 서식이 손실됩니다. 붙여 넣은 텍스트 만 지우고 이전 서식은 그대로 둡니다.

이상적으로이 솔루션은 모든 최신 브라우저 (예 : MSIE, Gecko, Chrome 및 Safari)에서 작동해야합니다.

MSIE에는가 clipboardData.getData()있지만 다른 브라우저와 유사한 기능을 찾을 수 없습니다.



답변

이 답변을 작성한 이후 상황이 변경되었습니다. 이제 Firefox는 버전 22에서 지원을 추가했으며 이제 모든 주요 브라우저는 붙여 넣기 이벤트에서 클립 보드 데이터 액세스를 지원합니다. 예를 들어 Nico Burns의 답변 을 참조하십시오 .

과거에는 일반적으로 크로스 브라우저 방식으로는 불가능했습니다. paste이벤트 를 통해 붙여 넣은 컨텐츠를 얻을 수있는 것이 이상적입니다 . 최근 브라우저 에서는 가능 하지만 일부 구형 브라우저 (특히 Firefox <22)에서는 불가능합니다.

구형 브라우저를 지원해야 할 때 Firefox 2 이상, IE 5.5 이상 및 Safari 또는 Chrome과 같은 WebKit 브라우저에서 작동하는 해킹과 관련이 있습니다. TinyMCE 및 CKEditor의 최신 버전은이 기술을 사용합니다.

  1. 키 누르기 이벤트 핸들러를 사용하여 ctrl-v / shift-in 이벤트 감지
  2. 이 핸들러에서 현재 사용자 선택을 저장하고 텍스트 영역 요소 (예 : -1000px)를 화면에 추가하고 텍스트 영역을 designMode끄고 호출 focus()하여 캐럿을 이동시키고 붙여 넣기를 효과적으로 리디렉션합니다.
  3. 이벤트 핸들러에서 매우 짧은 타이머 (1 밀리 초)를 설정하여 텍스트 영역 값을 저장하고 문서에서 텍스트 영역을 제거한 다음 designMode다시 켜고 사용자 선택을 복원하고 텍스트를 붙여 넣는 다른 함수를 호출 하십시오.

이는 키보드 붙여 넣기 이벤트에서만 작동하며 컨텍스트 또는 편집 메뉴에서 붙여 넣기는 수행되지 않습니다. 붙여 넣기 이벤트가 시작될 때 캐럿을 텍스트 영역 (적어도 일부 브라우저에서는)으로 리디렉션하기에는 너무 늦습니다.

Firefox 2를 지원해야 할 경우에는 해당 브라우저에서 WYSIWYG 편집기 iframe의 문서가 아닌 상위 문서에 텍스트 영역을 배치해야합니다.


답변

해결 방법 # 1 (일반 텍스트 만 해당하며 Firefox 22 이상이 필요함)

IE6 +, FF 22+, Chrome, Safari, Edge에서 작동합니다 (IE9 +에서만 테스트되었지만 하위 버전에서는 작동 함)

HTML 또는 Firefox <= 22 붙여 넣기를 지원해야하는 경우 솔루션 # 2를 참조하십시오.

HTML

<div id='editableDiv' contenteditable='true'>Paste</div>

자바 스크립트

function handlePaste (e) {
    var clipboardData, pastedData;

    // Stop data actually being pasted into div
    e.stopPropagation();
    e.preventDefault();

    // Get pasted data via clipboard API
    clipboardData = e.clipboardData || window.clipboardData;
    pastedData = clipboardData.getData('Text');

    // Do whatever with pasteddata
    alert(pastedData);
}

document.getElementById('editableDiv').addEventListener('paste', handlePaste);

JSFiddle : https://jsfiddle.net/swL8ftLs/12/

이 솔루션 getData은 비표준 인 함수에 ‘Text’매개 변수를 사용합니다 . 그러나 글을 쓰는 시점에 모든 브라우저에서 작동합니다.


솔루션 # 2 (HTML 및 Firefox <= 22에서 작동)

IE6 +, FF 3.5+, Chrome, Safari, Edge에서 테스트

HTML

<div id='div' contenteditable='true'>Paste</div>

자바 스크립트

var editableDiv = document.getElementById('editableDiv');

function handlepaste (e) {
    var types, pastedData, savedContent;

    // Browsers that support the 'text/html' type in the Clipboard API (Chrome, Firefox 22+)
    if (e && e.clipboardData && e.clipboardData.types && e.clipboardData.getData) {

        // Check for 'text/html' in types list. See abligh's answer below for deatils on
        // why the DOMStringList bit is needed. We cannot fall back to 'text/plain' as
        // Safari/Edge don't advertise HTML data even if it is available
        types = e.clipboardData.types;
        if (((types instanceof DOMStringList) && types.contains("text/html")) || (types.indexOf && types.indexOf('text/html') !== -1)) {

            // Extract data and pass it to callback
            pastedData = e.clipboardData.getData('text/html');
            processPaste(editableDiv, pastedData);

            // Stop the data from actually being pasted
            e.stopPropagation();
            e.preventDefault();
            return false;
        }
    }

    // Everything else: Move existing element contents to a DocumentFragment for safekeeping
    savedContent = document.createDocumentFragment();
    while(editableDiv.childNodes.length > 0) {
        savedContent.appendChild(editableDiv.childNodes[0]);
    }

    // Then wait for browser to paste content into it and cleanup
    waitForPastedData(editableDiv, savedContent);
    return true;
}

function waitForPastedData (elem, savedContent) {

    // If data has been processes by browser, process it
    if (elem.childNodes && elem.childNodes.length > 0) {

        // Retrieve pasted content via innerHTML
        // (Alternatively loop through elem.childNodes or elem.getElementsByTagName here)
        var pastedData = elem.innerHTML;

        // Restore saved content
        elem.innerHTML = "";
        elem.appendChild(savedContent);

        // Call callback
        processPaste(elem, pastedData);
    }

    // Else wait 20ms and try again
    else {
        setTimeout(function () {
            waitForPastedData(elem, savedContent)
        }, 20);
    }
}

function processPaste (elem, pastedData) {
    // Do whatever with gathered data;
    alert(pastedData);
    elem.focus();
}

// Modern browsers. Note: 3rd argument is required for Firefox <= 6
if (editableDiv.addEventListener) {
    editableDiv.addEventListener('paste', handlepaste, false);
}
// IE <= 8
else {
    editableDiv.attachEvent('onpaste', handlepaste);
}

JSFiddle : https://jsfiddle.net/nicoburns/wrqmuabo/23/

설명

onpaste이벤트 div에는 handlePaste함수가 첨부되어 event있으며 붙여 넣기 이벤트에 대한 객체 라는 단일 인수가 전달 되었습니다. clipboardData이 이벤트 의 속성 은 특히 비 브라우저에서 클립 보드 액세스를 가능하게하는 것입니다. IE에서는 window.clipboardDataAPI가 약간 다르지만 이에 해당합니다.

아래의 리소스 섹션을 참조하십시오.


handlepaste기능 :

이 기능에는 두 개의 분기가 있습니다.

제의 존재에 대한 검사 event.clipboardData그것의 여부를 체크 types재산권 ‘텍스트 / HTML’을 포함는 ( types있을 수 중 하나 DOMStringList은 USING 확인하는 contains방법, 또는 사용 점검 문자열 indexOf법). 이러한 조건이 모두 충족되면 ‘text / plain’대신 ‘text / html’을 제외하고 솔루션 # 1에서와 같이 진행합니다. 현재 Chrome 및 Firefox 22 이상에서 작동합니다.

이 방법이 지원되지 않는 경우 (다른 모든 브라우저)

  1. 요소의 내용을 DocumentFragment
  2. 요소 비우기
  3. waitForPastedData함수를 호출

waitforpastedata기능 :

이 기능은 먼저 붙여 넣은 데이터 (20ms 당 한 번)를 폴링합니다.이 데이터는 바로 나타나지 않기 때문에 필요합니다. 데이터가 나타 났을 때 :

  1. 편집 가능한 div (현재 붙여 넣은 데이터)의 innerHTML을 변수에 저장합니다.
  2. DocumentFragment에 저장된 내용을 복원
  3. 검색된 데이터와 함께 ‘processPaste’함수를 호출합니다.

processpaste기능 :

붙여 넣은 데이터로 임의의 작업을 수행합니다. 이 경우 우리는 데이터를 경고하기 만하면 원하는대로 할 수 있습니다. 붙여 넣은 데이터를 일종의 데이터 삭제 프로세스를 통해 실행하고 싶을 것입니다.


커서 위치 저장 및 복원

실제 상황에서는 선택 사항을 이전에 저장 한 후 나중에 복원 할 수 있습니다 ( contentEditable <div>에서 커서 위치 설정 ). 그런 다음 붙여 넣기 작업을 시작할 때 커서가 있던 위치에 붙여 넣은 데이터를 삽입 할 수 있습니다.

자원:

DocumentFragment 사용을 제안한 Tim Down에게 감사하고 clipboardData.types의 문자열 대신 DOMStringList를 사용하여 Firefox에서 오류를 발견 한 경우


답변

간단한 버전 :

document.querySelector('[contenteditable]').addEventListener('paste', (e) => {
    e.preventDefault();
    const text = (e.originalEvent || e).clipboardData.getData('text/plain');
    window.document.execCommand('insertText', false, text);
});

사용 clipboardData

데모 : http://jsbin.com/nozifexasu/edit?js,output

Edge, Firefox, Chrome, Safari, Opera 테스트되었습니다.

Document.execCommand ()는 더 이상 사용되지 않습니다 .


참고 : 서버 측에서도 입출력을 확인하십시오 ( PHP 스트립 태그 와 같은 )


답변

라이브 데모

Chrome / FF / IE11에서 테스트

이 브라우저는 <div>새로운 줄마다 요소를 추가한다는 Chrome / IE 성가심이 있습니다 . 이 이것에 대해 게시물입니다 이곳은 과은으로 설정하여 고정 할 수 의 contentEditable 로 요소를display:inline-block

강조 표시된 일부 HTML을 선택하여 여기에 붙여 넣으십시오.

function onPaste(e){
  var content;
  e.preventDefault();

  if( e.clipboardData ){
    content = e.clipboardData.getData('text/plain');
    document.execCommand('insertText', false, content);
    return false;
  }
  else if( window.clipboardData ){
    content = window.clipboardData.getData('Text');
    if (window.getSelection)
      window.getSelection().getRangeAt(0).insertNode( document.createTextNode(content) );
  }
}


/////// EVENT BINDING /////////
document.querySelector('[contenteditable]').addEventListener('paste', onPaste);
[contenteditable]{ 
  /* chroem bug: https://stackoverflow.com/a/24689420/104380 */
  display:inline-block;
  width: calc(100% - 40px);
  min-height:120px; 
  margin:10px;
  padding:10px;
  border:1px dashed green;
}

/* 
 mark HTML inside the "contenteditable"  
 (Shouldn't be any OFC!)'
*/
[contenteditable] *{
  background-color:red;
}
<div contenteditable></div>


답변

여기에 오프 스크린 텍스트 영역으로 Tim Downs 제안에 대한 약간의 개념 증명을 작성했습니다. 그리고 여기 코드가 있습니다.

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<script language="JavaScript">
 $(document).ready(function()
{

var ctrlDown = false;
var ctrlKey = 17, vKey = 86, cKey = 67;

$(document).keydown(function(e)
{
    if (e.keyCode == ctrlKey) ctrlDown = true;
}).keyup(function(e)
{
    if (e.keyCode == ctrlKey) ctrlDown = false;
});

$(".capture-paste").keydown(function(e)
{
    if (ctrlDown && (e.keyCode == vKey || e.keyCode == cKey)){
        $("#area").css("display","block");
        $("#area").focus();
    }
});

$(".capture-paste").keyup(function(e)
{
    if (ctrlDown && (e.keyCode == vKey || e.keyCode == cKey)){
        $("#area").blur();
        //do your sanitation check or whatever stuff here
        $("#paste-output").text($("#area").val());
        $("#area").val("");
        $("#area").css("display","none");
    }
});

});
</script>

</head>
<body class="capture-paste">

<div id="paste-output"></div>


    <div>
    <textarea id="area" style="display: none; position: absolute; left: -99em;"></textarea>
    </div>

</body>
</html>

전체 코드를 복사하여 하나의 html 파일에 붙여 넣고 클립 보드의 텍스트를 문서의 아무 곳에 나 붙여 넣으십시오 (ctrl-v 사용).

IE9와 Firefox, Chrome 및 Opera의 새 버전에서 테스트했습니다. 아주 잘 작동합니다. 또한이 기능을 심사하는 데 선호하는 키 조합을 사용할 수있는 것이 좋습니다. 물론 jQuery 소스를 포함시키는 것을 잊지 마십시오.

이 코드를 자유롭게 사용하고 개선 사항이나 문제가 발생하면 다시 게시하십시오. 또한 Javascript 개발자가 아니므로 무언가를 놓쳤을 수도 있습니다 (=> 자신의 증언을하십시오).


답변

l2aelba anwser를 기반으로 합니다. 이것은 FF, Safari, Chrome, IE (8,9,10 및 11)에서 테스트되었습니다.

    $("#editText").on("paste", function (e) {
        e.preventDefault();

        var text;
        var clp = (e.originalEvent || e).clipboardData;
        if (clp === undefined || clp === null) {
            text = window.clipboardData.getData("text") || "";
            if (text !== "") {
                if (window.getSelection) {
                    var newNode = document.createElement("span");
                    newNode.innerHTML = text;
                    window.getSelection().getRangeAt(0).insertNode(newNode);
                } else {
                    document.selection.createRange().pasteHTML(text);
                }
            }
        } else {
            text = clp.getData('text/plain') || "";
            if (text !== "") {
                document.execCommand('insertText', false, text);
            }
        }
    });


답변

이것은 setTimeout ()을 사용하지 않습니다.

훌륭한 기사를 사용 하여 크로스 브라우저 지원을 달성했습니다.

$(document).on("focus", "input[type=text],textarea", function (e) {
    var t = e.target;
    if (!$(t).data("EventListenerSet")) {
        //get length of field before paste
        var keyup = function () {
            $(this).data("lastLength", $(this).val().length);
        };
        $(t).data("lastLength", $(t).val().length);
        //catch paste event
        var paste = function () {
            $(this).data("paste", 1);//Opera 11.11+
        };
        //process modified data, if paste occured
        var func = function () {
            if ($(this).data("paste")) {
                alert(this.value.substr($(this).data("lastLength")));
                $(this).data("paste", 0);
                this.value = this.value.substr(0, $(this).data("lastLength"));
                $(t).data("lastLength", $(t).val().length);
            }
        };
        if (window.addEventListener) {
            t.addEventListener('keyup', keyup, false);
            t.addEventListener('paste', paste, false);
            t.addEventListener('input', func, false);
        }
        else {//IE
            t.attachEvent('onkeyup', function () {
                keyup.call(t);
            });
            t.attachEvent('onpaste', function () {
                paste.call(t);
            });
            t.attachEvent('onpropertychange', function () {
                func.call(t);
            });
        }
        $(t).data("EventListenerSet", 1);
    }
}); 

이 코드는 붙여 넣기 전에 선택 핸들로 확장됩니다.
데모