Program Club

콘텐츠 내에서 정확한 드래그 앤 드롭

proclub 2020. 12. 5. 10:49
반응형

콘텐츠 내에서 정확한 드래그 앤 드롭


설정

그래서, 저는 contenteditable div를 가지고 있습니다. 저는 WYSIWYG 편집기를 만들고 있습니다 : 굵은 체, 이탤릭체, 서식 등 무엇이든, 그리고 가장 최근에는 멋진 이미지 삽입 (캡션과 함께 멋진 상자에).

<a class="fancy" href="i.jpg" target="_blank">
    <img alt="" src="i.jpg" />
    Optional Caption goes Here!
</a>

사용자는 내가 제시 한 대화 상자와 함께 이러한 멋진 이미지를 추가합니다. 세부 사항을 입력하고 이미지를 업로드 한 다음 다른 편집기 기능과 마찬가지로 document.execCommand('insertHTML',false,fancy_image_html);사용자가 선택하는 데 사용합니다.

원하는 기능

이제 내 사용자가 멋진 이미지를 만들 수 있으므로 이미지를 움직일 수 있어야합니다. 사용자는 이미지 (팬시 상자 및 모두)를 클릭하고 드래그하여 콘텐츠 편집 가능 내에서 원하는 위치에 배치 할 수 있어야합니다. 원하는 경우 단락 사이 또는 단락 내에서 두 단어 사이로 이동할 수 있어야합니다.

나에게 희망을주는 것

명심하세요-콘텐츠 편집이 가능한 평범한 오래된 <img>태그는 이미이 멋진 드래그 앤 드롭 기능으로 사용자 에이전트에 의해 축복을 받았습니다. 기본적 <img>으로 원하는 곳으로 태그를 끌어다 놓을 수 있습니다 . 기본 드래그 앤 드롭 작업은 꿈처럼 작동합니다.

그래서,이 기본 동작이 이미 우리 <img>친구들 에게 어떻게 그렇게 굉장하게 작동하는지 고려할 때 -그리고 저는이 동작을 조금 더 확장하여 좀 더 많은 HTML을 포함하고 싶습니다-이것은 쉽게 가능할 것 같습니다.

지금까지의 나의 노력

먼저 <a>draggable 속성을 사용하여 멋진 태그를 설정하고 contenteditable을 비활성화했습니다 (필요한지 확실하지 않지만 꺼져있는 것 같음).

<a class="fancy" [...] draggable="true" contenteditable="false">

그런 다음 사용자가 여전히 멋진 <a>상자 에서 이미지를 드래그 할 수 있기 때문에 CSS 작업을 수행해야했습니다. 저는 Chrome에서 작업하고 있으므로 다른 접두사도 사용했지만 -webkit- 접두사 만 표시합니다.

.fancy {
    -webkit-user-select:none;
    -webkit-user-drag:element; }
    .fancy>img {
        -webkit-user-drag:none; }

이제 사용자는 전체 멋진 상자를 드래그 할 수 있으며, 부분적으로 희미해진 클릭 드래그 표현 이미지가이를 반영합니다. 이제 전체 상자를 선택하고 있음을 알 수 있습니다. :)

다양한 CSS 속성의 여러 조합을 시도해 보았지만 위의 콤보는 나에게 의미가 있고 가장 잘 작동하는 것 같습니다.

이 CSS만으로도 브라우저가 전체 요소를 드래그 가능한 항목으로 사용하여 사용자에게 내가 꿈꾸던 기능을 자동으로 부여하기에 충분하길 바랬 습니다.하지만 그보다 더 복잡해 보입니다.

HTML5의 JavaScript 드래그 앤 드롭 API

이 드래그 앤 드롭 기능 필요 이상으로 복잡해 보입니다 .

그래서 저는 DnD api 문서에 깊이 들어가기 시작했고 이제 막혔습니다. 그래서 여기에 내가 준비한 것이 있습니다 (예, jQuery).

$('.fancy')
    .bind('dragstart',function(event){
        //console.log('dragstart');
        var dt=event.originalEvent.dataTransfer;
        dt.effectAllowed = 'all';
        dt.setData('text/html',event.target.outerHTML);
    });

$('.myContentEditable')
    .bind('dragenter',function(event){
        //console.log('dragenter');
        event.preventDefault();
    })
    .bind('dragleave',function(event){
        //console.log('dragleave');
    })
    .bind('dragover',function(event){
        //console.log('dragover');
        event.preventDefault();
    })
    .bind('drop',function(event){
        //console.log('drop');      
        var dt = event.originalEvent.dataTransfer;
        var content = dt.getData('text/html');
        document.execCommand('insertHTML',false,content);
        event.preventDefault();
    })
    .bind('dragend',function(event){ 
        //console.log('dragend');
    });

그래서 여기에 제가 갇혀있는 부분있습니다. 이것은 거의 완벽하게 작동합니다. 거의 완벽합니다. 나는 끝까지 모든 것이 작동합니다. 드롭 이벤트에서 이제 드롭 위치에 삽입하려는 멋진 상자의 HTML 콘텐츠에 액세스 할 수 있습니다. 지금해야 할 일은 올바른 위치에 삽입하는 것입니다!

문제는 올바른 드롭 위치 나 삽입 할 방법을 찾을 수 없다는 것입니다. 나는 내 멋진 상자를 , 또는 적어도 어떤 종류의 드롭 위치 값으로 덤프 할 일종의 'dropLocation' 객체를 찾고 싶었dropEvent.dropLocation.content=myFancyBoxHTML; 습니다. 여기에 콘텐츠를 넣는 내 자신의 방법을 찾을 수 있습니까? 나는 무엇을 받았습니까?

내가 완전히 잘못하고 있습니까? 내가 뭔가를 완전히 놓치고 있습니까?

나는 document.execCommand('insertHTML',false,content);내가 할 수있을 것이라고 예상 한대로 사용하려고 했지만, 불행히도 선택 캐럿이 내가 바라는대로 정확한 드롭 위치에 있지 않기 때문에 여기서는 실패했다.

의 모든 것을 주석 처리 event.preventDefault();하면 선택 캐럿이 표시되고 원하는대로 사용자가 드롭을 준비 할 때 콘텐츠 편집 가능 위로 드래그하면 작은 선택 캐럿이 문자 사이를 따라 움직이는 것을 볼 수 있음을 발견했습니다. 사용자의 커서 및 놓기 작업을 따라-선택 캐럿이 정확한 놓기 위치를 나타냄을 사용자에게 나타냅니다. 이 선택 캐럿의 위치가 필요합니다.

몇 가지 실험을 통해 드롭 이벤트와 dragend 이벤트 중에 execCommand-insertHTML'ing을 시도했습니다. 드롭 선택 캐럿이있는 곳에 HTML을 삽입하지 않고 대신 드래그 작업 전에 선택한 위치를 사용합니다.

드래그 오버 중에 선택 캐럿이 보이기 때문에 계획을 해치했습니다.

잠시 동안 나는 드래그 오버 이벤트 에서 브라우저가 모든 선택 마커를 지속적으로 삭제하고 삽입 지점에 하나를 추가하기 위해 <span class="selection-marker">|</span>, 바로 뒤에 와 같은 임시 마커를 삽입 $('.selection-marker').remove();하려고했습니다. 기본적으로 삽입 지점이있는 곳에 마커 하나만 남겨 둡니다. 물론 계획은이 임시 마커를 내가 가지고있는 드래그 된 콘텐츠로 대체하는 것이 었습니다.

물론이 중 어느 것도 작동하지 않았습니다. 계획대로 보이는 선택 캐럿에 선택 마커를 삽입 할 수 없었습니다. 다시, execCommand-insertedHTML은 드래그 작업 이전에 선택 캐럿이있는 곳에 위치했습니다.

성냄. 그래서 내가 무엇을 놓쳤습니까? 어떻게 되나요?

끌어서 놓기 작업의 정확한 위치를 가져 오거나 삽입하려면 어떻게합니까? 분명히 이것이 드래그 앤 드롭의 일반적인 작업이라고 생각합니다. 분명히 어떤 종류의 중요하고 노골적인 세부 사항을 간과했을 것입니다. JavaScript에 깊이 들어가야 했습니까, 아니면 드래그 가능, 드롭 가능, 컨텐츠 편집 가능 및 멋진 CSS3와 같은 속성으로 만이 작업을 수행 할 수있는 방법이 있습니까?

나는 여전히 사냥 중입니다-여전히 고장 중입니다. 내가 실패한 것을 알아내는 즉시 다시 게시 할 것입니다 :)


The Hunt Continues (원래 게시물 이후 편집)


Farrukh는 좋은 제안을 올렸습니다.

console.log( window.getSelection().getRangeAt(0) );

선택 캐럿이 실제로 어디에 있는지 확인합니다. 나는 이것을 dragover 이벤트에 넣었는데 , 선택 캐럿이 contenteditable의 편집 가능한 콘텐츠 사이를 눈에 띄게 뛰어 다니고 있다고 생각할 때입니다.

아아, 반환되는 Range 개체는 끌어서 놓기 작업 전에 선택 캐럿에 속하는 오프셋 인덱스를보고합니다.

용감한 노력이었습니다. 감사합니다 Farrukh.

그래서 여기서 무슨 일이 일어나고 있습니까? 나는 내가 돌아 다니는 작은 선택 캐럿이 전혀 선택 캐럿이 아니라는 느낌을 받고 있습니다! 사기꾼 인 것 같아요!

추가 검사시!

밝혀졌다, 그것은 사기꾼이다! 실제 선택 caret는 전체 끌기 작업을 수행하는 동안 자리에 남아! 작은 도깨비를 볼 수 있습니다!

MDN Drag and Drop Docs를 읽고 있었는데 다음을 발견했습니다.

당연히 드래그 오버 이벤트 주위로 삽입 마커를 이동해야 할 수도 있습니다. 다른 마우스 이벤트와 마찬가지로 이벤트의 clientX 및 clientY 속성을 사용하여 마우스 포인터의 위치를 ​​결정할 수 있습니다.

Yikes, 이것은 내가 clientXclientY를 기반으로 스스로 알아 내야한다는 것을 의미합니까 ? 마우스 좌표를 사용하여 선택 캐럿의 위치를 ​​직접 결정하십니까? 무서운!!

나는 내일 그렇게 할 것입니다-나 자신이나 다른 누군가가 이것을 읽고 제정신의 해결책을 찾을 수 없다면 :)


드래곤 드롭

나는 말도 안되는 양의 장난을했다. 그래서 너무 많은 jsFiddling.

이것은 강력하거나 완전한 솔루션이 아닙니다. 나는 결코 하나를 생각하지 못할 수도 있습니다. 더 나은 해결책이 있다면, 저는 귀를 기울이고 있습니다. 이런 식으로하고 싶지는 않았지만 지금까지 밝혀 낼 수 있었던 유일한 방법입니다. 다음 jsFiddle과 내가 구토하려는 정보는 특정 WAMP 설정 및 컴퓨터에서 특정 버전의 Firefox 및 Chrome과 함께이 특정 인스턴스에서 나를 위해 일했습니다. 귀하의 웹 사이트에서 작동하지 않을 때 나에게 울지 마십시오. 이 드래그 앤 드롭 쓰레기는 분명히 모든 사람을위한 것입니다.

jsFiddle : Chase Moskal의 드래곤 드롭

So, I was boring my girlfriend's brains out, and she thought I kept saying "dragon drop" when really, I was just saying "drag-and-drop". It stuck, so that's what I call my little JavaScript buddy I've created for handling these drag-and-drop situations.

밝혀졌습니다-그것은 약간의 악몽입니다. HTML5 Drag-and-Drop API는 언뜻보기에도 끔찍합니다. 당신이있어 길을 이해하고 받아들이는 시작 그런 다음, 당신은 거의 그것에 따뜻한 가정 일에 당신이 파이어 폭스와 크롬은 자신의 특별한에서이 사양에 대해 가지 방법으로 그런 다음, 무서운 악몽이 실제로 그것을 어떻게 실현 .. 당신의 모든 필요를 완전히 무시하는 것 같습니다. "잠깐, 지금 끌고있는 요소가 무엇입니까? 해당 정보를 어떻게 얻습니까?이 끌기 작업을 어떻게 취소합니까?이 상황에 대한이 특정 브라우저의 고유 한 기본 처리를 어떻게 중지 할 수 있습니까?"와 같은 질문을합니다. ... 질문에 대한 답변 : "당신은 혼자입니다, 패배자! 뭔가 작동 할 때까지 계속 해킹하세요!".

그래서, 여기 에 여러 contenteditable 내부, 주변 및 사이에서 임의의 HTML 요소의 정확한 드래그 앤 드롭을 수행 한 방법 이 있습니다. (참고 : 모든 세부 사항에 대해 완전히 심층적으로 다루지는 않을 것입니다. jsFiddle을 확인해야합니다. 시간이 제한되어 있으므로 경험에서 기억하는 관련성이있는 것처럼 보이는 세부 정보 만 훑어 보겠습니다. )

내 솔루션

  • 먼저 드래그 가능한 항목 (fancybox)에 CSS를 적용했습니다. 우리 user-select:none; user-drag:element;는 멋진 상자에 필요 했고, 그 다음에는 특히 user-drag:none;멋진 상자 내의 이미지에 필요 했습니다 (그리고 다른 요소, 왜 안됩니까?). 안타깝게도 파이어 폭스에는 충분하지 않았습니다 draggable="false". 이미지를 드래그 할 수 없도록 이미지에 속성 을 명시 적으로 설정해야했습니다.
  • 다음으로 속성 draggable="true"dropzone="copy"contenteditables를 적용했습니다 .

draggables (fancyboxes)에 대한 핸들러를 바인딩합니다 dragstart. 우리는 HTML을 드래그 할 것이라고 생각하도록 속일 필요가 있기 때문에 HTML ''의 빈 문자열을 복사하도록 dataTransfer를 설정했지만 기본 동작을 취소합니다. 때로는 기본 동작이 어떻게 든 미끄러 져서 중복이 발생하므로 (우리가 직접 삽입하는 것처럼) 이제 최악의 글리치는 드래그가 실패 할 때 삽입되는 ''(공백)입니다. 자주 실패하는 기본 동작에 의존 할 수 없었기 때문에 이것이 가장 다재다능한 솔루션이라는 것을 알았습니다.

DD.$draggables.off('dragstart').on('dragstart',function(event){
    var e=event.originalEvent;
    $(e.target).removeAttr('dragged');
    var dt=e.dataTransfer,
        content=e.target.outerHTML;
    var is_draggable = DD.$draggables.is(e.target);
    if (is_draggable) {
        dt.effectAllowed = 'copy';
        dt.setData('text/plain',' ');
        DD.dropLoad=content;
        $(e.target).attr('dragged','dragged');
    }
});

dropzones에는 dragleave및에 대한 핸들러를 바인딩합니다 drop. dragleave 핸들러는 Firefox에서만 존재합니다. Firefox에서와 같이 콘텐츠 편집 가능 외부로 드래그하려고 할 때 드래그 드롭이 작동하므로 (Chrome에서는 기본적으로 거부) Firefox 전용 relatedTarget. 성냄.

Chrome과 Firefox는 Range 개체를 획득하는 방법이 다르기 때문에 드롭 이벤트의 각 브라우저에 대해 다르게 수행하기 위해 노력해야했습니다. Chrome은 마우스 좌표를 기반으로 범위를 구축 하지만 ( 맞습니다) Firefox는 이벤트 데이터에이를 제공합니다. document.execCommand('insertHTML',false,blah)우리가 드롭을 처리하는 방법으로 밝혀졌습니다. 아, 언급하는 것을 잊었습니다 dataTransfer.getData(). Chrome에서 드래그 스타트 세트 HTML을 가져올 수 없습니다 . 사양에서 일종의 이상한 버그 인 것 같습니다. 파이어 폭스는 그 사양을 헛소리로 부르고 어쨌든 데이터를 우리에게 제공하지만 크롬은 그렇지 않습니다. 그래서 우리는 뒤로 구부리고 콘텐츠를 전역으로 설정하고 지옥을 통과하여 모든 기본 동작을 죽입니다 ...

DD.$dropzones.off('dragleave').on('dragleave',function(event){
    var e=event.originalEvent;

    var dt=e.dataTransfer;
    var relatedTarget_is_dropzone = DD.$dropzones.is(e.relatedTarget);
    var relatedTarget_within_dropzone = DD.$dropzones.has(e.relatedTarget).length>0;
    var acceptable = relatedTarget_is_dropzone||relatedTarget_within_dropzone;
    if (!acceptable) {
        dt.dropEffect='none';
        dt.effectAllowed='null';
    }
});
DD.$dropzones.off('drop').on('drop',function(event){
    var e=event.originalEvent;

    if (!DD.dropLoad) return false;
    var range=null;
    if (document.caretRangeFromPoint) { // Chrome
        range=document.caretRangeFromPoint(e.clientX,e.clientY);
    }
    else if (e.rangeParent) { // Firefox
        range=document.createRange(); range.setStart(e.rangeParent,e.rangeOffset);
    }
    var sel = window.getSelection();
    sel.removeAllRanges(); sel.addRange(range);

    $(sel.anchorNode).closest(DD.$dropzones.selector).get(0).focus(); // essential
    document.execCommand('insertHTML',false,'<param name="dragonDropMarker" />'+DD.dropLoad);
    sel.removeAllRanges();

    // verification with dragonDropMarker
    var $DDM=$('param[name="dragonDropMarker"]');
    var insertSuccess = $DDM.length>0;
    if (insertSuccess) {
        $(DD.$draggables.selector).filter('[dragged]').remove();
        $DDM.remove();
    }

    DD.dropLoad=null;
    DD.bindDraggables();
    e.preventDefault();
});

좋아, 이거 지겨워. 나는 이것에 대해 내가 원하는 모든 것을 썼다. 나는 그것을 하루라고 부르고 있으며 중요한 것을 생각하면 이것을 업데이트 할 수 있습니다.

감사합니다 여러분. //추적.


네이티브 JS 솔루션에서 이것을보고 싶었 기 때문에 모든 jQuery 종속성을 제거하기 위해 약간 노력했습니다. 누군가를 도울 수 있기를 바랍니다.

먼저 마크 업

    <div class="native_receiver" style="border: 2px solid red;padding: 5px;" contenteditable="true" >
      WAITING  FOR STUFF
    </div>
    <div class="drawer" style="border: 2px solid #AAE46A; padding: 10px">
      <span class="native_drag" data-type="dateselector" contenteditable="false" draggable="true" style="border: 2px solid rgba(0,0,0,0.2);padding:4px; margin:2px">
        Block 1
      </span>
      <span class="native_drag" data-type="dateselector" contenteditable="false" draggable="true" style="border: 2px solid rgba(0,0,0,0.2);padding:4px; margin:2px">
        Second Blk
      </span>
    </div>

그런 다음 일부 도우미

    function addClass( elem, className ){
        var classNames = elem.className.split( " " )
        if( classNames.indexOf( className ) === -1 ){
            classNames.push( className )
        }
        elem.className = classNames.join( " " )
    }
    function selectElem( selector ){
        return document.querySelector( selector )
    }
    function selectAllElems( selector ){
        return document.querySelectorAll( selector )
    }
    function removeElem( elem ){
         return elem ? elem.parentNode.removeChild( elem ) : false
    }

그런 다음 실제 방법

    function nativeBindDraggable( elems = false ){
        elems = elems || selectAllElems( '.native_drag' );
        if( !elems ){
            // No element exists, abort
            return false;
        }else if( elems.outerHTML ){
            // if only a single element, put in array
            elems = [ elems ];
        }
        // else it is html-collection already (as good as array)

        for( let i = 0 ; i < elems.length ; i++ ){
            // For every elem in list, attach or re-attach event handling
            elems[i].dataset.transferreference = `transit_${ new Date().getTime() }`;
            elems[i].ondragstart = function(e){
                if (!e.target.id){
                    e.target.id = (new Date()).getTime();
                }

                window.inTransferMarkup = e.target.outerHTML;
                window.transferreference = elems[i].dataset.transferreference;
                addClass( e.target, 'dragged');
            };
        };
    }

    function nativeBindWriteRegion( elems = false ){
        elems = elems || selectAllElems( '.native_receiver' );
        if( !elems ){
            // No element exists, abort
            return false;
        }else if( elems.outerHTML ){
            // if only a single element, put in array
            elems = [ elems ];
        }
        // else it is html-collection

        for( let i = 0 ; i < elems.length ; i++ ){
            elems[i].ondragover = function(e){
                e.preventDefault();
                return false;
            };
            elems[i].ondrop = function(e){
                receiveBlock(e);
            };
        }
    }

    function receiveBlock(e){
        e.preventDefault();
        let content = window.inTransferMarkup;

        window.inTransferMarkup = "";

        let range = null;
        if (document.caretRangeFromPoint) { // Chrome
            range = document.caretRangeFromPoint(e.clientX, e.clientY);
        }else if (e.rangeParent) { // Firefox
            range = document.createRange();
            range.setStart(e.rangeParent, e.rangeOffset);
        }
        let sel = window.getSelection();
        sel.removeAllRanges(); 
        sel.addRange( range );
        e.target.focus();

        document.execCommand('insertHTML',false, content);
        sel.removeAllRanges();

        // reset draggable on all blocks, esp the recently created
        nativeBindDraggable(
          document.querySelector(
            `[data-transferreference='${window.transferreference}']`
          )
        );
        removeElem( selectElem( '.dragged' ) );
        return false;
    }

그리고 마지막으로 인스턴스화

nativeBindDraggable();
nativeBindWriteRegion();

아래는 작동하는 스 니펫입니다.

function addClass( elem, className ){
            var classNames = elem.className.split( " " )
            if( classNames.indexOf( className ) === -1 ){
                classNames.push( className )
            }
            elem.className = classNames.join( " " )
        }
        function selectElem( selector ){
            return document.querySelector( selector )
        }
        function selectAllElems( selector ){
            return document.querySelectorAll( selector )
        }
        function removeElem( elem ){
             return elem ? elem.parentNode.removeChild( elem ) : false
        }
        
      
    	function nativeBindDraggable( elems = false ){
    		elems = elems || selectAllElems( '.native_drag' );
    		if( !elems ){
    			// No element exists, abort
    			return false;
    		}else if( elems.outerHTML ){
    			// if only a single element, put in array
    			elems = [ elems ];
    		}
    		// else it is html-collection already (as good as array)
            
    		for( let i = 0 ; i < elems.length ; i++ ){
    			// For every elem in list, attach or re-attach event handling
    			elems[i].dataset.transferreference = `transit_${ new Date().getTime() }`;
    			elems[i].ondragstart = function(e){
    				if (!e.target.id){
    					e.target.id = (new Date()).getTime();
    				}

    				window.inTransferMarkup = e.target.outerHTML;
    				window.transferreference = elems[i].dataset.transferreference;
    				addClass( e.target, 'dragged');
    			};
    		};
    	}
        
    	function nativeBindWriteRegion( elems = false ){
    		elems = elems || selectAllElems( '.native_receiver' );
    		if( !elems ){
    			// No element exists, abort
    			return false;
    		}else if( elems.outerHTML ){
    			// if only a single element, put in array
    			elems = [ elems ];
    		}
    		// else it is html-collection
    		
    		for( let i = 0 ; i < elems.length ; i++ ){
    			elems[i].ondragover = function(e){
    				e.preventDefault();
    				return false;
    			};
    			elems[i].ondrop = function(e){
    				receiveBlock(e);
    			};
    		}
    	}
        
        function receiveBlock(e){
    		e.preventDefault();
    		let content = window.inTransferMarkup;
    		
    		window.inTransferMarkup = "";
    		
    		let range = null;
    		if (document.caretRangeFromPoint) { // Chrome
    			range = document.caretRangeFromPoint(e.clientX, e.clientY);
    		}else if (e.rangeParent) { // Firefox
    			range = document.createRange();
    			range.setStart(e.rangeParent, e.rangeOffset);
    		}
    		let sel = window.getSelection();
    		sel.removeAllRanges(); 
    		sel.addRange( range );
    		e.target.focus();
    		
    		document.execCommand('insertHTML',false, content);
    		sel.removeAllRanges();
    		
            // reset draggable on all blocks, esp the recently created
    		nativeBindDraggable(
              document.querySelector(
                `[data-transferreference='${window.transferreference}']`
              )
            );
    		removeElem( selectElem( '.dragged' ) );
    		return false;
    	}


    nativeBindDraggable();
    nativeBindWriteRegion();
        <div class="native_receiver" style="border: 2px solid red;padding: 5px;" contenteditable="true" >
          WAITING  FOR STUFF
        </div>
        <div class="drawer" style="border: 2px solid #AAE46A; padding: 10px">
          <span class="native_drag" data-type="dateselector" contenteditable="false" draggable="true" style="border: 2px solid rgba(0,0,0,0.2);padding:4px; margin:2px">
            Block 1
          </span>
          <span class="native_drag" data-type="dateselector" contenteditable="false" draggable="true" style="border: 2px solid rgba(0,0,0,0.2);padding:4px; margin:2px">
            Second Blk
          </span>
        </div>


  1. 이벤트 dragstart; dataTransfer.setData("text/html", "<div class='whatever'></div>");
  2. 이벤트 드롭 : var me = this; setTimeout(function () { var el = me.element.getElementsByClassName("whatever")[0]; if (el) { //do stuff here, el is your location for the fancy img } }, 0);

참고 URL : https://stackoverflow.com/questions/14678451/precise-drag-and-drop-within-a-contenteditable

반응형