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


299

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

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

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

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

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


이 모든 답변은 텍스트 내용을 얻는 방법을 설명합니다. 이미지 컨텐츠 또는 파일 컨텐츠를 가져 오려면 훨씬 더 많은 작업이 필요합니다. 제목을 "JavaScript get sanitized text clipboard data ..."로 변경할 수 있습니다.
1.21 기가 와트

1
니코가 말한 것처럼 : event.clipboardData.getData('Text')나를 위해 일했습니다.
Andre Elrico

document.addEventListener('paste'...나를 위해 일했지만 사용자가 페이지의 다른 곳에 붙여 넣기를 원하면 충돌이 발생했습니다. 그런 다음 시도 myCanvasElement.addEventListener('paste'...했지만 작동하지 않았습니다. 결국 나는 효과가 있다고 생각했다 myCanvasElement.parentElement.addEventListener('paste'....
라이언

답변:


149

이 답변을 작성한 이후 상황이 변경되었습니다. 이제 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
와, 고마워! 그래도 매우 정교한 해킹 인 것 같습니다. ;-) 특히 designMode 및 selection을 3 단계에서 조금 더 설명해 주시겠습니까? 고마워요!
Alex

5
나는 당신이 그것을 물어보고 끔찍한 느낌이 들었습니다. 내가 말했듯이, 그것은 매우 관련이 있습니다 : 관련된 모든 문제를 간략하게 설명 할 시간이 없기 때문에 TinyMCE 또는 CKEditor의 출처를 살펴 보는 것이 좋습니다. 간단히 말해 designMode의 부울 속성이며 documentwhen 전체 페이지를 편집 할 수 true있습니다. WYSIWYG 편집기는 일반적으로 designMode편집 가능한 분할 창이 있는 iframe을 사용합니다 . 콘텐츠를 편집기에 붙여 넣는 것처럼 사용자 선택 사항을 저장하고 복원하는 방법은 IE와 다른 브라우저에서 한 가지 방법으로 수행됩니다. TextRangeIE 및 Range다른 브라우저 에서을 가져와야 합니다.
Tim Down

6
@Samuel : paste이벤트를 사용하여 감지 할 수 있지만 일반적으로 붙여 넣기를 다른 요소로 리디렉션하기에는 너무 늦기 때문에이 핵은 작동하지 않습니다. 대부분의 편집기에서 단점은 사용자가 붙여 넣을 수있는 대화 상자를 표시하는 것입니다.
Tim Down

6
이것에 대한 더 많은 정보 : Firefox는 paste이벤트 에서 다른 요소로 초점을 옮길 수는 없지만 요소 의 내용을 지우고 나중에 복원 할 수 있도록 변수에 저장할 수 있습니다. 이 컨테이너가 div(아마도 잘 작동하는 iframe경우) 일반 dom 메소드를 사용하여 붙여 넣은 내용을 순환하거나을 사용하여 문자열로 가져올 수 있습니다 innerHTML. 그런 다음의 이전 내용을 복원하고 div원하는 내용을 삽입 할 수 있습니다. 아, 위와 같은 타이머 해킹을 사용해야합니다. TinyMCE가 이것을하지 않는 것에 놀랐습니다 ...
Nico Burns

8
@ResistDesign : 동의하지 않습니다. 현명한 API가 부족한 것을 보완하는 우아하고 복잡한 방법입니다. 붙여 넣기 이벤트에서 붙여 넣은 콘텐츠를 직접 가져 오는 것이 좋습니다 . 일부 브라우저에서는 제한적으로 가능합니다 .
Tim Down

318

해결 방법 # 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에서 오류를 발견 한 경우


4
흥미 롭군 나는 과거에 이것을 시도했지만 브라우저에서 작동하지 않았다고 생각했지만, 당신이 옳다고 확신합니다. 여러 가지 이유로 기존 컨텐츠를 사용하는 DocumentFragment대신 기존 컨텐츠를 이동하는 것이 좋습니다 innerHTML. 먼저 기존 이벤트 핸들러를 유지하십시오. 둘째, 저장 및 복원 innerHTML이 이전 DOM의 동일한 사본을 작성한다고 보장하지는 않습니다. 셋째, Range마커 요소를 추가하거나 텍스트 오프셋을 계산하는 데 어려움을 겪지 않고 선택 항목을 저장할 수 있습니다 (이를 사용하면해야 할 일 innerHTML).
Tim Down

3
실제로 내용이없는 플래시 (FONC?)가 있으며, 붙여 넣은 내용을 처리하는 데 시간이 걸리면 분명히 나빠질 것입니다. Btw, 왜 DocumentFragmentIE에서 추출 이 고통 스럽습니까? Range를 사용하지 않으면 다른 브라우저에서와 동일하지만 extractContents()어떤 경우에도 대안보다 간결하지 않습니다. Rangy를 사용 하여 브라우저에서 일관되고 멋지게 유지하는 jsfiddle.net/bQeWC/4 기술의 예를 구현했습니다 .
팀 다운

1
@ Martin : 의견에 게시 한 jsFiddle 데모가 도움이 될 수 있습니다.
Tim Down

1
Windows의 Firefox 28 (적어도)에서는 더 이상 작동하지 않는 것 같습니다. 절대 waitforpastedata기능에서
벗어나지

1
참고 : Edge는 이제 text/htmlW3C 클립 보드 API를 사용하여 MIME 유형의 데이터 읽기를 지원합니다 . 과거에는 그러한 시도가 예외를 던졌습니다. 따라서 더 이상 Edge에 대한이 해결 방법 / 해킹이 필요하지 않습니다.
Jenny O'Reilly

130

간단한 버전 :

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 스트립 태그 와 같은 )


4
정말 잘이 작동하지만 IE의 어떤 버전은 이벤트 :( 다양한 솔루션에서하여 clipboardData에 액세스 할 수 있습니다,하지만,이 높아야한다!
에릭 목재

1
IE의 클립 보드 데이터를 다른 방식으로 얻을 수있는 것처럼 보이므로 IE를 감지하면 프롬프트 대체 대신 해당 데이터를 사용할 수 있습니다 : msdn.microsoft.com/en-us/library/ie/ms535220(v = vs.85) .aspx
Andrew

4
지금까지 가장 좋은 크로스 브라우저 답변. IE 코드와 완벽한 코드 만 추가하면됩니다.
Arturo

6
이것은 IE (ah, sweet, contrary IE)에서 작동합니다window.clipboardData.getData('Text');
Benjineer

9
e.preventDefault(); if (e.clipboardData) { content = (e.originalEvent || e).clipboardData.getData('text/plain'); document.execCommand('insertText', false, content); } else if (window.clipboardData) { content = window.clipboardData.getData('Text'); document.selection.createRange().pasteHTML(content); }
Yukulelix

26

라이브 데모

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>


1
일반 텍스트 기능으로 붙여 넣기가 필요했습니다. IE9 및 IE10에서 테스트되었으며 훌륭하게 작동합니다. 말할 것도없이 주요 브라우저에서도 작동합니다. 감사합니다.
Savas Vedova 2016 년

2
코드에 버그가 포함되어 있습니다. if (e.originalEvent.clipboardData)는 e.originalEvent가 해당 시점에 있는지 알 수 없으므로 NPE를 유발할 수 있습니다
Sebastian

15

여기에 오프 스크린 텍스트 영역으로 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 개발자가 아니므로 무언가를 놓쳤을 수도 있습니다 (=> 자신의 증언을하십시오).


Mac은 ctrl-v로 붙여 넣지 않고 cmd-v를 사용합니다. 따라서 17 대신 ctrlKey = 91을 설정하십시오
Jeremy T

2
또는 항상 91이 아닐 수도 있습니다. stackoverflow.com/questions/3834175/… 그럼에도 불구하고 jQuery가 모든 것을 처리한다고 확신합니다.
Jeremy T

3
: e.ctrlKey 또는 e.metaKey는 자바 스크립트 DOM하지 jQuery를의 일부입니다 developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
rvighne

2
마우스 오른쪽 버튼을 클릭하고 붙여 넣을 때 이것이 효과가 있다고 생각하지 않습니다. 많은 사람들이 그러한 접근 방식을 취합니다.
Eric Wood

10

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);
            }
        }
    });

IE에 붙여 넣을 때 줄 바꿈을 유지하는 방법이 있습니까?
Stay at November

10

이것은 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);
    }
}); 

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


+1 나는 각자 자신의 자리가 있다고 생각하지만 Nico Burns보다이 것을 더 좋아합니다.
n0nag0n

5

붙여 넣은 텍스트정리하고 현재 선택한 텍스트를 붙여 넣은 텍스트바꾸는 경우 문제는 매우 간단합니다.

<div id='div' contenteditable='true' onpaste='handlepaste(this, event)'>Paste</div>

JS :

function handlepaste(el, e) {
  document.execCommand('insertText', false, e.clipboardData.getData('text/plain'));
  e.preventDefault();
}

이것이 작동하는 데모 페이지를 제공 할 수 있습니까? 나는 그것을 시도하고 그것은 작동하지 않습니다
수직 동기화

5

이것은 onpaste 이벤트와 돌연변이 관찰자를 지원하는 모든 브라우저에서 작동합니다.

이 솔루션은 텍스트 만 가져 오는 것 이상의 단계를 거치며 실제로 붙여 넣은 내용을 요소에 붙여 넣기 전에 편집 할 수 있습니다.

컨텐츠 편집 가능, 붙여 넣기 이벤트 (모든 주요 브라우저에서 지원) 및 돌연변이 관찰자 (Chrome, Firefox 및 IE11 +에서 지원)를 사용하여 작동합니다.

1 단계

contenteditable로 HTML 요소 만들기

<div contenteditable="true" id="target_paste_element"></div>

2 단계

Javascript 코드에서 다음 이벤트를 추가하십시오.

document.getElementById("target_paste_element").addEventListener("paste", pasteEventVerifierEditor.bind(window, pasteCallBack), false);

돌연변이 관찰자가 비동기 적으로 호출되므로 pasteCallBack을 바인딩해야합니다.

3 단계

코드에 다음 함수를 추가하십시오

function pasteEventVerifierEditor(callback, e)
{
   //is fired on a paste event. 
    //pastes content into another contenteditable div, mutation observer observes this, content get pasted, dom tree is copied and can be referenced through call back.
    //create temp div
    //save the caret position.
    savedCaret = saveSelection(document.getElementById("target_paste_element"));

    var tempDiv = document.createElement("div");
    tempDiv.id = "id_tempDiv_paste_editor";
    //tempDiv.style.display = "none";
    document.body.appendChild(tempDiv);
    tempDiv.contentEditable = "true";

    tempDiv.focus();

    //we have to wait for the change to occur.
    //attach a mutation observer
    if (window['MutationObserver'])
    {
        //this is new functionality
        //observer is present in firefox/chrome and IE11
        // select the target node
        // create an observer instance
        tempDiv.observer = new MutationObserver(pasteMutationObserver.bind(window, callback));
        // configuration of the observer:
        var config = { attributes: false, childList: true, characterData: true, subtree: true };

        // pass in the target node, as well as the observer options
        tempDiv.observer.observe(tempDiv, config);

    }   

}



function pasteMutationObserver(callback)
{

    document.getElementById("id_tempDiv_paste_editor").observer.disconnect();
    delete document.getElementById("id_tempDiv_paste_editor").observer;

    if (callback)
    {
        //return the copied dom tree to the supplied callback.
        //copy to avoid closures.
        callback.apply(document.getElementById("id_tempDiv_paste_editor").cloneNode(true));
    }
    document.body.removeChild(document.getElementById("id_tempDiv_paste_editor"));

}

function pasteCallBack()
{
    //paste the content into the element.
    restoreSelection(document.getElementById("target_paste_element"), savedCaret);
    delete savedCaret;

    pasteHtmlAtCaret(this.innerHTML, false, true);
}   


saveSelection = function(containerEl) {
if (containerEl == document.activeElement)
{
    var range = window.getSelection().getRangeAt(0);
    var preSelectionRange = range.cloneRange();
    preSelectionRange.selectNodeContents(containerEl);
    preSelectionRange.setEnd(range.startContainer, range.startOffset);
    var start = preSelectionRange.toString().length;

    return {
        start: start,
        end: start + range.toString().length
    };
}
};

restoreSelection = function(containerEl, savedSel) {
    containerEl.focus();
    var charIndex = 0, range = document.createRange();
    range.setStart(containerEl, 0);
    range.collapse(true);
    var nodeStack = [containerEl], node, foundStart = false, stop = false;

    while (!stop && (node = nodeStack.pop())) {
        if (node.nodeType == 3) {
            var nextCharIndex = charIndex + node.length;
            if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
                range.setStart(node, savedSel.start - charIndex);
                foundStart = true;
            }
            if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
                range.setEnd(node, savedSel.end - charIndex);
                stop = true;
            }
            charIndex = nextCharIndex;
        } else {
            var i = node.childNodes.length;
            while (i--) {
                nodeStack.push(node.childNodes[i]);
            }
        }
    }

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

function pasteHtmlAtCaret(html, returnInNode, selectPastedContent) {
//function written by Tim Down

var sel, range;
if (window.getSelection) {
    // IE9 and non-IE
    sel = window.getSelection();
    if (sel.getRangeAt && sel.rangeCount) {
        range = sel.getRangeAt(0);
        range.deleteContents();

        // Range.createContextualFragment() would be useful here but is
        // only relatively recently standardized and is not supported in
        // some browsers (IE9, for one)
        var el = document.createElement("div");
        el.innerHTML = html;
        var frag = document.createDocumentFragment(), node, lastNode;
        while ( (node = el.firstChild) ) {
            lastNode = frag.appendChild(node);
        }
        var firstNode = frag.firstChild;
        range.insertNode(frag);

        // Preserve the selection
        if (lastNode) {
            range = range.cloneRange();
            if (returnInNode)
            {
                range.setStart(lastNode, 0); //this part is edited, set caret inside pasted node.
            }
            else
            {
                range.setStartAfter(lastNode); 
            }
            if (selectPastedContent) {
                range.setStartBefore(firstNode);
            } else {
                range.collapse(true);
            }
            sel.removeAllRanges();
            sel.addRange(range);
        }
    }
} else if ( (sel = document.selection) && sel.type != "Control") {
    // IE < 9
    var originalRange = sel.createRange();
    originalRange.collapse(true);
    sel.createRange().pasteHTML(html);
    if (selectPastedContent) {
        range = sel.createRange();
        range.setEndPoint("StartToStart", originalRange);
        range.select();
    }
}
}

코드가하는 일 :

  1. 누군가 Ctrl-V, 컨텍스트 메뉴 또는 다른 수단을 사용하여 붙여 넣기 이벤트를 시작합니다.
  2. 붙여 넣기 이벤트에서 contenteditable이있는 새 요소가 작성됩니다 (contenteditable이있는 요소에 높은 권한이 있음)
  3. 대상 요소의 캐럿 위치가 저장됩니다.
  4. 초점이 새로운 요소로 설정됩니다
  5. 컨텐츠는 새 요소에 붙여 넣어 DOM에 렌더링됩니다.
  6. 돌연변이 관찰자는 이것을 포착합니다 (dom 트리 및 내용에 대한 모든 변경 사항을 등록합니다). 그런 다음 돌연변이 이벤트를 발생시킵니다.
  7. 붙여 넣은 내용의 돔이 변수에 복제되어 콜백으로 반환됩니다. 임시 요소가 파괴됩니다.
  8. 콜백은 복제 된 DOM을받습니다. 캐럿이 복원되었습니다. 대상에 추가하기 전에이를 편집 할 수 있습니다. 요소. 이 예제에서는 캐럿을 저장 / 복원하고 HTML을 요소에 붙여 넣기 위해 Tim Downs 함수를 사용하고 있습니다.


많은 감사 팀 아래 참조 답변이 게시물 :

붙여 넣기 이벤트시 문서에 붙여 넣은 내용 가져 오기


4

나를 위해 해결책은 텍스트 입력에 붙여 넣을 경우 이벤트 리스너를 추가하여 이벤트를 붙여 넣는 것입니다. 붙여 넣기 이벤트는 입력 변경의 텍스트보다 먼저 발생하므로 내 붙여 넣기 처리기 내부에서 붙여 넣기에 발생한 입력 상자의 변경 사항을 확인하는 지연된 함수를 만듭니다.

onPaste: function() {
    var oThis = this;
    setTimeout(function() { // Defer until onPaste() is done
        console.log('paste', oThis.input.value);
        // Manipulate pasted input
    }, 1);
}

2
불행히도 공포는 우리 직업 설명의 일부입니다.) 그러나 나는 이것이 해킹이며 다른 모든 옵션이 소진 될 때만 해킹을 사용해야한다는 데 동의합니다.
Lex

4

이것은 Nico의 답변에 대한 의견이 너무 길어서 Firefox에서 더 이상 작동하지 않는다고 생각하고 (사설에 따라) Safari에서 나에게는 효과가 없었습니다.

먼저, 이제 클립 보드에서 직접 읽을 수있는 것으로 보입니다. 다음과 같은 코드 대신

if (/text\/plain/.test(e.clipboardData.types)) {
    // shouldn't this be writing to elem.value for text/plain anyway?
    elem.innerHTML = e.clipboardData.getData('text/plain');
}

사용하다:

types = e.clipboardData.types;
if (((types instanceof DOMStringList) && types.contains("text/plain")) ||
    (/text\/plain/.test(types))) {
    // shouldn't this be writing to elem.value for text/plain anyway?
    elem.innerHTML = e.clipboardData.getData('text/plain');
}

Firefox에는 구현하지 않는 types필드 가 있기 때문 입니다.DOMStringListtest

다음 Firefox는 포커스가 contenteditable=true필드 에 있지 않으면 붙여 넣기를 허용하지 않습니다 .

마지막으로, Firefox는 포커스가 (뿐만 아니라 입력) 에 있지 않은 한 확실하게 붙여 넣기를 허용 textarea하지 않습니다 contenteditable=true.

  • 아니 display:none
  • 아니 visibility:hidden
  • 0이 아닌 크기

텍스트 필드를 숨기려고 JS VNC 에뮬레이터에서 붙여 넣기 작업을 수행 할 수있었습니다 (즉, 원격 클라이언트로 이동 textarea하여 붙여 넣을 등 이 없었 습니다). 위의 텍스트 필드를 숨기려고하면 때때로 효과가있는 증상이 나타 났지만 일반적으로 두 번째 붙여 넣기 (또는 동일한 데이터를 두 번 붙여 넣을 수 없도록 필드가 지워졌을 때)에서 필드 포커스가 없어지고 제대로 회복되지 않는 것으로 나타났습니다. 그럼에도 불구하고 focus(). 내가 생각해 낸 해결책은에 넣고 z-order: -1000, 만들고 display:none, 1 x 1px로 만들고 모든 색상을 투명하게 설정하는 것이 었습니다. 왝.

사파리에서, 위의 두 번째 부분, 즉 당신이 있어야합니다, 적용 textarea하지 않은가 display:none.


브라우저 렌더링 엔진에서 작업하는 개발자는 작업하는 기능에 대한 메모를 작성하는 데 사용할 수있는 페이지 또는 공간이 설명서 사이트에 있어야합니다. 예를 들어, 붙여 넣기 기능을 수행 한 경우 "표시가 없거나 가시성이 숨겨 지거나 크기가 0이면 붙여 넣기가 작동하지 않습니다."라고 덧붙입니다.
1.21 기가 와트


3

간단한 해결책 :

document.onpaste = function(e) {
    var pasted = e.clipboardData.getData('Text');
    console.log(pasted)
}

2

이것은 나를 위해 일했다 :

function onPasteMe(currentData, maxLen) {
    // validate max length of pasted text
    var totalCharacterCount = window.clipboardData.getData('Text').length;
}

<input type="text" onPaste="return onPasteMe(this, 50);" />

2
function myFunct( e ){
    e.preventDefault();

    var pastedText = undefined;
    if( window.clipboardData && window.clipboardData.getData ){
    pastedText = window.clipboardData.getData('Text');
} 
else if( e.clipboardData && e.clipboardData.getData ){
    pastedText = e.clipboardData.getData('text/plain');
}

//work with text

}
document.onpaste = myFunct;

1

이 방법으로이 작업을 수행 할 수 있습니다.

붙여 넣기 전후 이벤트에이 jQuery 플러그인을 사용하십시오.

$.fn.pasteEvents = function( delay ) {
    if (delay == undefined) delay = 20;
    return $(this).each(function() {
        var $el = $(this);
        $el.on("paste", function() {
            $el.trigger("prepaste");
            setTimeout(function() { $el.trigger("postpaste"); }, delay);
        });
    });
};

이제이 플러그인을 사용할 수 있습니다 :

$('#txt').on("prepaste", function() { 

    $(this).find("*").each(function(){

        var tmp=new Date.getTime();
        $(this).data("uid",tmp);
    });


}).pasteEvents();

$('#txt').on("postpaste", function() { 


  $(this).find("*").each(function(){

     if(!$(this).data("uid")){
        $(this).removeClass();
          $(this).removeAttr("style id");
      }
    });
}).pasteEvents();

설명

먼저 모든 기존 요소의 uid를 data 속성으로 설정하십시오.

그런 다음 모든 노드 POST PASTE 이벤트를 비교하십시오. 따라서 비교하면 uid가 있기 때문에 새로 삽입 된 요소를 식별 한 다음 새로 작성된 요소에서 style / class / id 속성을 제거하면 기존 형식을 유지할 수 있습니다.


1
$('#dom').on('paste',function (e){
    setTimeout(function(){
        console.log(e.currentTarget.value);
    },0);
});

1

브라우저가 내용을 편집 가능한 div에 평소처럼 붙여 넣은 다음 붙여 넣은 후에 사용자 정의 텍스트 스타일에 사용되는 span 요소를 텍스트 자체와 교체하십시오. 이것은 인터넷 익스플로러와 내가 시도한 다른 브라우저에서 정상적으로 작동하는 것 같습니다 ...

$('[contenteditable]').on('paste', function (e) {
    setTimeout(function () {
        $(e.target).children('span').each(function () {
            $(this).replaceWith($(this).text());
        });
    }, 0);
});

이 솔루션은 실행중인 것으로 가정 jQuery를 것을 당신이 당신의 콘텐츠를 편집 된 div의에 서식 텍스트를 원하지 않는다 .

장점은 매우 간단하다는 것입니다.


span태그인가? 모든 태그에 대한 질문이라고 생각합니다.
Alexis Wilke 2016 년

1

이 솔루션은 html 태그를 대체합니다. 간단하고 크로스 브라우저입니다. 이 jsfiddle을 확인하십시오 : http://jsfiddle.net/tomwan/cbp1u2cx/1/ , 핵심 코드 :

var $plainText = $("#plainText");
var $linkOnly = $("#linkOnly");
var $html = $("#html");

$plainText.on('paste', function (e) {
    window.setTimeout(function () {
        $plainText.html(removeAllTags(replaceStyleAttr($plainText.html())));
    }, 0);
});

$linkOnly.on('paste', function (e) {
    window.setTimeout(function () {
        $linkOnly.html(removeTagsExcludeA(replaceStyleAttr($linkOnly.html())));
    }, 0);
});

function replaceStyleAttr (str) {
    return str.replace(/(<[\w\W]*?)(style)([\w\W]*?>)/g, function (a, b, c, d) {
        return b + 'style_replace' + d;
    });
}

function removeTagsExcludeA (str) {
    return str.replace(/<\/?((?!a)(\w+))\s*[\w\W]*?>/g, '');
}

function removeAllTags (str) {
    return str.replace(/<\/?(\w+)\s*[\w\W]*?>/g, '');
}

주의 사항 :이 솔루션은 '<< >>'와 같은 문자열을 필터링 할 수 없으므로 뒷면의 xss 필터에 대한 작업을 수행해야합니다


서버의 XSS 파일링은 JavaScript 필터가 제대로 작동하는지 여부와 관련이 없습니다. 해커는 어쨌든 JS 필터링의 100 %를 우회합니다.
Alexis Wilke 2016 년

정규식을 사용하여 HTML을 구문 분석 / 변환하지 마십시오!
SubliemeSiem

0

이것은 위에 게시 된 기존 코드이지만 IE 용으로 업데이트했습니다. 버그는 기존 텍스트를 선택하고 붙여 넣을 때 선택한 내용을 삭제하지 않습니다. 이것은 아래 코드로 수정되었습니다.

selRange.deleteContents(); 

아래의 전체 코드를 참조하십시오

$('[contenteditable]').on('paste', function (e) {
    e.preventDefault();

    if (window.clipboardData) {
        content = window.clipboardData.getData('Text');        
        if (window.getSelection) {
            var selObj = window.getSelection();
            var selRange = selObj.getRangeAt(0);
            selRange.deleteContents();                
            selRange.insertNode(document.createTextNode(content));
        }
    } else if (e.originalEvent.clipboardData) {
        content = (e.originalEvent || e).clipboardData.getData('text/plain');
        document.execCommand('insertText', false, content);
    }        
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.