Précis de Glisser et Déposer dans un contenteditable

L'Installation

Donc, j'ai un contenteditable div -- je suis en train de faire un éditeur WYSIWYG: gras, italique, mise en forme, que ce soit, et plus récemment: l'insertion de la fantaisie des images (dans une jolie boîte, avec une légende).

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

L'utilisateur ajoute ces fantaisie des images avec une boîte de dialogue je vous les présente avec: ils remplissez les détails, télécharger l'image, et puis comme les autres fonctions de l'éditeur, j'utilise document.execCommand('insertHTML',false,fancy_image_html); à plop à la sélection de l'utilisateur.

Fonctionnalités Souhaitées

Donc, maintenant que mon utilisateur peut plop dans une fantaisie de l'image -- dont ils ont besoin pour être en mesure de le déplacer.
L'utilisateur doit être en mesure de cliquer et de faire glisser l'image (de fantaisie boîte et tout et tout) pour le placer n'importe où qu'ils s'il vous plaît dans le contenteditable. Ils doivent être en mesure de le déplacer entre les paragraphes, ou même à l'intérieur des paragraphes, entre deux mots, s'ils le souhaitent.

Ce qui me donne de l'espoir

Garder à l'esprit -- dans un contenteditable, plain old <img> les balises sont déjà béni par l'agent utilisateur avec cette jolie glisser-déposer. Par défaut, vous pouvez faire glisser et déposer <img> tags partout où vous s'il vous plaît; la valeur par défaut de glisser-déposer l'opération se comporte comme un rêve.

Aussi, compte tenu de la façon dont ce comportement par défaut fonctionne déjà de manière smashingly sur notre <img> copains -- et je ne veux étendre ce comportement un peu pour inclure un peu plus de HTML -- cela semble être quelque chose qui devrait être facilement possible.

Mes Efforts Jusqu'À Présent

Tout d'abord, j'ai créé ma fantaisie <a> de la balise avec l'attribut draggable, et désactivé contenteditable (pas sûr si c'est nécessaire, mais il me semble qu'il peut aussi bien être éteint):

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

Ensuite, parce que l'utilisateur pourrait encore faire glisser l'image de la fantaisie <a> boîte, j'ai eu à faire un peu de CSS. Je travaille dans Chrome, donc je ne fais que vous montrer l'-webkit - préfixes, bien que j'ai utilisé les autres aussi.

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

Maintenant, l'utilisateur peut faire glisser l'ensemble de la fantaisie de la boîte, et le petit partiellement décoloré cliquer-glisser de la représentation de l'image reflète ce, je m'aperçois que je suis à ramasser l'ensemble de la zone de maintenant 🙂

J'ai essayé plusieurs combinaisons de différentes propriétés CSS, le combo semble faire sens pour moi, et semble mieux fonctionner.

J'espérais que cette CSS, seul, serait suffisant pour le navigateur à utiliser la totalité de l'élément de l'élément déplaçable, automatiquement l'octroi de l'utilisateur de la fonctionnalité j'ai rêvé de... par contre, Il n'apparaît plus compliqué que ça.

HTML5 JavaScript API de Glisser-Déposer

Ce faire Glisser et Déposer des trucs semble plus compliqué qu'il doit être.

Donc, j'ai commencé à entrer en profondeur dans Mdn api docs, et maintenant je suis coincé. Donc, voici ce que j'ai affublé (oui, 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');
    });

Donc voilà où j'en suis coincé: Ce presque complètement œuvres. Presque complètement. J'ai tout qui fonctionne, jusqu'à la toute fin. Dans la liste des événements, j'ai maintenant accès à la fantaisie de la zone de contenu HTML que je suis en train d'avoir inséré à l'emplacement de dépôt. Tout ce que je dois faire maintenant, c'est l'insérer au bon endroit!

Le problème est je ne peux pas trouver le bon emplacement de dépôt, ou de toute façon à l'insérer à elle. J'ai été en espérant y trouver une sorte de 'dropLocation" objet pour vider mon costume de boîte à, quelque chose comme dropEvent.dropLocation.content=myFancyBoxHTML;, ou peut-être, au moins, un certain type d'emplacement de dépôt de valeurs qui trouver ma propre façon de mettre le contenu là-bas?
Suis-je donné quelque chose?

Je fais complètement fausse? Suis-je complètement à côté de quelque chose?

J'ai essayé d'utiliser document.execCommand('insertHTML',false,content); comme je l'ai prévu, je devrais être en mesure de, mais malheureusement il me manque ici, comme la sélection de l'accent circonflexe n'est pas situé précisément à la chute de l'emplacement que j'avais de l'espoir.

J'ai découvert que si je commente toutes les event.preventDefault();'s, la sélection curseur devient visible, et qu'on pourrait l'espérer, lorsque l'utilisateur se prépare à la baisse, passant la faites glisser sur la contenteditable, la petite sélection de signe peut être vu courir le long de entre les caractères suivants de l'utilisateur curseur et déplacer -- indiquant à l'utilisateur que la sélection du curseur représente le précis de l'emplacement de dépôt. J'ai besoin de l'emplacement de cette sélection curseur.

Quelques expériences, j'ai essayé execCommand-insertHTML pratiquent pendant la chute de l'événement, et la dragend événement -- ni insérer le code HTML où l'abandon de sélection-l'accent circonflexe a été, au contraire, il utilise tout ce emplacement a été choisi avant l'opération de glisser.

Parce que la sélection curseur est visible pendant dragover, j'ai échafaudé un plan.

Pendant un certain temps, j'ai essayé, dans l'événement dragover, pour insérer un marqueur temporaire, comme <span class="selection-marker">|</span>, juste après $('.selection-marker').remove();, dans une tentative pour le navigateur en permanence (pendant dragover) supprimer tous les marqueurs de sélection, puis en ajoutant un au point d'insertion-pour l'essentiel, laissant un marqueur à chaque fois que le point d'insertion sont, à tout moment. Le plan de cours, a été puis remplacer cette inscription temporaire avec la traîné de contenu que j'ai.

Rien de tout cela fonctionné, bien sûr: je ne pouvais pas obtenir la sélection-marqueur à insérer, à l'apparence visible de sélection lambda comme prévu, encore une fois, la execCommand-insertedHTML lui-même placé là où le curseur de sélection a été, avant l'opération de glisser.

Huff. Alors, qu'ai-je manqué? Comment est-il fait?

Comment puis-je obtenir, ou insérez-le dans, la localisation précise d'un glisser-déposer de l'opération?
Je sens que c'est, de toute évidence, une opération commune entre les drag-and-drop -- oui, je dois avoir oublié un important et le plus flagrant de détail de quelque sorte? Je n'ai même pas avoir à pénétrer profondément dans le JavaScript, ou peut-être il y a un moyen de le faire juste avec des attributs comme draggable, droppable, contenteditable, et certains fancydancy CSS3?

Je suis toujours à la recherche -- encore bricoler -- je vais poster dès que je trouve ce que je ai été en situation d'échec 🙂


La Chasse Continue (modifications après le post original)


Farrukh posté une bonne suggestion d'utilisation:

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

Pour voir où le curseur de sélection est en réalité. Je laissa dans la dragover événement, qui est que quand je me figure la sélection caret est visibily sautillant entre mon contenu modifiable dans le contenteditable.

Hélas, l'objet Range qui est retourné, les rapports de décalage des indices qui appartiennent à la sélection de signe avant de le glisser-déposer.

C'était un vaillant effort. Grâce Farrukh.

Alors que ce passe ici? Je suis arriver a la sensation que la petite sélection caret je vois sautiller, n'est-ce pas le curseur de sélection à tous! Je pense que c'est un imposteur!

Après Une Inspection Plus Poussée!

Se trouve, il est un imposteur! Le réel de sélection curseur reste en place au cours de l'ensemble de l'opération de glisser! Vous pouvez voir le petit bougre!

Je lisais MDN faire Glisser et Déposer des Docs, et trouvé ceci:

Naturellement, vous pouvez avoir besoin de déplacer le marqueur d'insertion autour d'un événement dragover ainsi. Vous pouvez utiliser l'événement de clientX et clientY propriétés comme avec d'autres événements de la souris pour déterminer l'emplacement du pointeur de la souris.

Aïe, est-ce à dire que je suis censé pour comprendre, pour moi-même, basée sur clientX et clientY?? En utilisant les coordonnées de la souris pour déterminer l'emplacement de la sélection caret moi-même? Effrayant!!

Je vais regarder dans le faire demain, à moins que de moi-même, ou quelqu'un d'autre ici la lecture de ce, peut trouver un sane solution 🙂

  • +1 Bien posé la question!
  • Lorsque l'image des gouttes à ce stade de la sélection curseur a l'endroit où vous souhaitez insérer. n'est-il pas? si c'est pour utiliser ce stackoverflow.com/questions/1891444/...
  • Il a semblé comme une piste prometteuse. J'ai de la console.journal avais window.getSelection().getRangeAt(0); pendant dragover (qui est l'endroit où le curseur de sélection est visible), et l'objet de la Plage, il retourne montre les valeurs d'index de où la sélection caret était à l'origine, avant de le glisser-déposer. Merci bien, c'était une bonne pensée.
InformationsquelleAutor ChaseMoskal | 2013-02-03