Comment Trello accède-t-il au presse-papiers de l'utilisateur?

936

Lorsque vous survolez une carte dans Trello et appuyez sur Ctrl+ C, l'URL de cette carte est copiée dans le presse-papiers. comment font-ils ça?

Pour autant que je sache, aucun film Flash n'est impliqué. J'ai Flashblock installé et l'onglet réseau Firefox n'affiche aucun film Flash chargé. (C'est la méthode habituelle, par exemple, par ZeroClipboard.)

Comment parviennent-ils à cette magie?

(En ce moment, je pense que j'avais une révélation: vous ne pouvez pas sélectionner de texte sur la page, donc je suppose qu'ils ont un élément invisible, où ils créent une sélection de texte via le code JavaScript, et Ctrl+ Cdéclenche le comportement par défaut du navigateur, copiant cet invisible la valeur de texte du nœud.)

Boldewyn
la source
22
Si vous regardez le DOM en direct, il y a un div avec la classe "clipboard-container". Lorsque vous maintenez la touche Ctrl enfoncée, elle est remplie d'une zone de texte (et est supprimée lorsque vous retirez la touche Ctrl). Je suppose que votre épiphanie est correcte. Je ne sais pas exactement où ils stockent l'URL par carte
Ian
@Ian, oui, je peux le confirmer, c'est exactement comme ça que ça a fonctionné. Merci de l'avoir trouvé! (Je ne me soucie pas de l'endroit où l'URL est stockée. Je m'intéressais à la technologie du presse-papiers sans flash.)
Boldewyn
2
J'ai recherché le profil de Daniel, et il semble que c'est un développeur Trello. (Je me demandais d'où il venait la source Coffeescript.) Il a donc un avantage injuste ;-) Merci quand même!
Boldewyn
1
Je n'ai pas l'intention de nuire à l'ingéniosité de cette technique, c'est assez intelligent; mais je ne peux pas m'empêcher de penser que c'est, au mieux, mal publicisé / documenté, et au pire, une expérience utilisateur assez choquante. Certes, ce n'est pas un choc invasif (car je ne me souviens pas d'un moment où j'ai copié accidentellement l'URL de la carte), mais en tant qu'utilisateur de longue date de Trello, je n'avais absolument aucune idée que cela existait.
Michael Wales
3
@MichaelWales Cette fonctionnalité a été ajoutée il y a 5 jours; nous le testons toujours, et s'il semble fonctionner, il sera documenté comme un raccourci clavier.
Daniel LeCheminant

Réponses:

1547

Divulgation: j'ai écrit le code que Trello utilise ; le code ci-dessous est le code source réel que Trello utilise pour accomplir l'astuce du presse-papiers.


Nous n'accédons pas réellement au presse-papiers de l'utilisateur, mais nous aidons un peu l'utilisateur en sélectionnant quelque chose d'utile lorsqu'il appuie sur Ctrl+ C.

On dirait que vous l'avez compris; nous profitons du fait que lorsque vous voulez appuyer sur Ctrl+ C, vous devez d'abord appuyer sur la Ctrltouche. Lorsque la Ctrltouche est enfoncée, nous insérons une zone de texte qui contient le texte que nous voulons finir dans le presse-papiers, et sélectionnons tout le texte qu'il contient, de sorte que la sélection est entièrement définie lorsque la Ctouche est enfoncée. (Ensuite, nous masquons la zone de texte lorsque la Ctrlclé est levée)

Plus précisément, Trello fait ceci:

TrelloClipboard = new class
  constructor: ->
    @value = ""

    $(document).keydown (e) =>
      # Only do this if there's something to be put on the clipboard, and it
      # looks like they're starting a copy shortcut
      if !@value || !(e.ctrlKey || e.metaKey)
        return

      if $(e.target).is("input:visible,textarea:visible")
        return

      # Abort if it looks like they've selected some text (maybe they're trying
      # to copy out a bit of the description or something)
      if window.getSelection?()?.toString()
        return

      if document.selection?.createRange().text
        return

      _.defer =>
        $clipboardContainer = $("#clipboard-container")
        $clipboardContainer.empty().show()
        $("<textarea id='clipboard'></textarea>")
        .val(@value)
        .appendTo($clipboardContainer)
        .focus()
        .select()

    $(document).keyup (e) ->
      if $(e.target).is("#clipboard")
        $("#clipboard-container").empty().hide()

  set: (@value) ->

Dans le DOM, nous avons

<div id="clipboard-container"><textarea id="clipboard"></textarea></div>

CSS pour le presse-papiers:

#clipboard-container {
  position: fixed;
  left: 0px;
  top: 0px;
  width: 0px;
  height: 0px;
  z-index: 100;
  display: none;
  opacity: 0;
}
#clipboard {
  width: 1px;
  height: 1px;       
  padding: 0px;
}

... et le CSS fait en sorte que vous ne pouvez pas réellement voir la zone de texte quand elle apparaît ... mais elle est suffisamment "visible" pour copier.

Lorsque vous survolez une carte, elle appelle

TrelloClipboard.set(cardUrl)

... alors l'assistant presse-papiers sait quoi sélectionner lorsque la Ctrltouche est enfoncée.

Daniel LeCheminant
la source
3
Impressionnant! Mais comment avez-vous Mac OS - "écoutez-vous" la touche Commande ici?
Suman
28
Il convient de noter qu'une méthode similaire fonctionne aussi bien pour capturer du contenu collé
Michael Robinson
17
Cela semble être mauvais pour les utilisateurs de clavier - chaque fois que vous essayez de copier (ou ctrl + clic pour ouvrir dans une autre fenêtre, ou Ctrl + F pour rechercher, etc.), votre focus est déplacé quelque part sans rapport.
Adam A
2
+1. Beaucoup de choses intéressantes se passent dans cette réponse. J'aime que vous ayez réellement partagé le code source. Mais ce que je pensais être intelligent était l'explication réelle du processus utilisé pour fournir la fonctionnalité ctrl + c. À mon avis, il était très intelligent de profiter du fait que ctrl et c ne peuvent pas être enfoncés en même temps en commençant à préparer le c lorsque ctrl est enfoncé. J'ai vraiment aimé cette approche.
Travis J
8
N'hésitez pas à utiliser js2coffee.org pour traduire l'original en js si vous le souhaitez.
Alexandr Kurilin
79

J'ai en fait construit une extension Chrome qui fait exactement cela, et pour toutes les pages Web. Le code source est sur GitHub .

Je trouve trois bugs avec l'approche de Trello, que je connais parce que je les ai rencontrés moi-même :)

La copie ne fonctionne pas dans ces scénarios:

  1. Si vous avez déjà Ctrlappuyé, puis survolé un lien et appuyé C, la copie ne fonctionne pas.
  2. Si votre curseur se trouve dans un autre champ de texte de la page, la copie ne fonctionne pas.
  3. Si votre curseur se trouve dans la barre d'adresse, la copie ne fonctionne pas.

J'ai résolu le problème n ° 1 en ayant toujours une plage cachée, plutôt que d'en créer une lorsque l'utilisateur frappe Ctrl/ Cmd.

J'ai résolu # 2 en effaçant temporairement la sélection de longueur nulle, en enregistrant la position du curseur, en faisant la copie et en restaurant la position du curseur.

Je n'ai pas encore trouvé de correctif pour # 3 :) (Pour plus d'informations, consultez le problème ouvert dans mon projet GitHub).

Dhruv Vemula
la source
10
Vous avez donc fait cela de la même manière que Trello. Doux quand de telles choses convergent
Thomas Ahle
@ThomasAhle, que voulez-vous dire?
Pacerier
7
@Pacerier, je suppose que Thomas a fait allusion à l' évolution convergente - "... évolution indépendante de caractéristiques similaires dans des espèces de lignées différentes"
yoniLavi
Holy Cow, vous pouvez ouvrir un nouveau chat sur ce sujet
Carkod
20

Avec l'aide du code de raincoat ( lien vers GitHub ), j'ai réussi à obtenir une version en cours d'exécution accédant au presse-papiers avec JavaScript simple.

function TrelloClipboard() {
    var me = this;

    var utils = {
        nodeName: function (node, name) {
            return !!(node.nodeName.toLowerCase() === name)
        }
    }
    var textareaId = 'simulate-trello-clipboard',
        containerId = textareaId + '-container',
        container, textarea

    var createTextarea = function () {
        container = document.querySelector('#' + containerId)
        if (!container) {
            container = document.createElement('div')
            container.id = containerId
            container.setAttribute('style', [, 'position: fixed;', 'left: 0px;', 'top: 0px;', 'width: 0px;', 'height: 0px;', 'z-index: 100;', 'opacity: 0;'].join(''))
            document.body.appendChild(container)
        }
        container.style.display = 'block'
        textarea = document.createElement('textarea')
        textarea.setAttribute('style', [, 'width: 1px;', 'height: 1px;', 'padding: 0px;'].join(''))
        textarea.id = textareaId
        container.innerHTML = ''
        container.appendChild(textarea)

        textarea.appendChild(document.createTextNode(me.value))
        textarea.focus()
        textarea.select()
    }

    var keyDownMonitor = function (e) {
        var code = e.keyCode || e.which;
        if (!(e.ctrlKey || e.metaKey)) {
            return
        }
        var target = e.target
        if (utils.nodeName(target, 'textarea') || utils.nodeName(target, 'input')) {
            return
        }
        if (window.getSelection && window.getSelection() && window.getSelection().toString()) {
            return
        }
        if (document.selection && document.selection.createRange().text) {
            return
        }
        setTimeout(createTextarea, 0)
    }

    var keyUpMonitor = function (e) {
        var code = e.keyCode || e.which;
        if (e.target.id !== textareaId || code !== 67) {
            return
        }
        container.style.display = 'none'
    }

    document.addEventListener('keydown', keyDownMonitor)
    document.addEventListener('keyup', keyUpMonitor)
}

TrelloClipboard.prototype.setValue = function (value) {
    this.value = value;
}

var clip = new TrelloClipboard();
clip.setValue("test");

Le seul problème est que cette version ne fonctionne qu'avec Chrome. La plateforme Trello prend en charge tous les navigateurs. Qu'est-ce qui me manque?

Sovled grâce à VadimIvanov.

Voir un exemple de travail: http://jsfiddle.net/AGEf7/

Felix
la source
@ don41382 cela ne fonctionne pas correctement sur Safari (au moins la version Mac). Sous bon, je veux dire qu'il copie, mais vous devez appuyer deux fois sur cmd + C.
Vadim Ivanov
@VadimIvanov True! Est-ce que quelqu'un sait pourquoi?
Felix
1
@ don41382 Je ne sais pas exactement pourquoi, mais j'ai trouvé une solution. Vous avez un bug mineur, onKeyDown la première instruction devrait être if (! (E.ctrlKey || e.metaKey)) {return; } Cela signifie que nous devons préparer la zone de texte pour la copie sur la touche MetaKey enfoncée (c'est ainsi que les gars de trello ont fait un tour). Ceci est un code de trello.com gist.github.com/fustic/10870311
Vadim Ivanov
@VadimIvanov Merci. Je vais le réparer ci-dessus.
Felix
1
Cela ne fonctionnait pas dans FF 33.1 car il el.innerTextn'était pas défini, j'ai donc changé la dernière ligne de la clipboard()fonction clip.setValue(el.innerText || el.textContent);pour plus de compatibilité entre les navigateurs. lien: jsfiddle.net/AGEf7/31
RevanProdigalKnight
7

Le code de Daniel LeCheminant n'a pas fonctionné pour moi après l'avoir converti de CoffeeScript en JavaScript ( js2coffee ). Il a continué à bombarder la _.defer()ligne.

J'ai supposé que c'était quelque chose à voir avec les reports jQuery, alors je l'ai changé $.Deferred()et ça fonctionne maintenant. Je l'ai testé dans Internet Explorer 11, Firefox 35 et Chrome 39 avec jQuery 2.1.1. L'utilisation est la même que celle décrite dans le post de Daniel.

var TrelloClipboard;

TrelloClipboard = new ((function () {
    function _Class() {
        this.value = "";
        $(document).keydown((function (_this) {
            return function (e) {
                var _ref, _ref1;
                if (!_this.value || !(e.ctrlKey || e.metaKey)) {
                    return;
                }
                if ($(e.target).is("input:visible,textarea:visible")) {
                    return;
                }
                if (typeof window.getSelection === "function" ? (_ref = window.getSelection()) != null ? _ref.toString() : void 0 : void 0) {
                    return;
                }
                if ((_ref1 = document.selection) != null ? _ref1.createRange().text : void 0) {
                    return;
                }
                return $.Deferred(function () {
                    var $clipboardContainer;
                    $clipboardContainer = $("#clipboard-container");
                    $clipboardContainer.empty().show();
                    return $("<textarea id='clipboard'></textarea>").val(_this.value).appendTo($clipboardContainer).focus().select();
                });
            };
        })(this));

        $(document).keyup(function (e) {
            if ($(e.target).is("#clipboard")) {
                return $("#clipboard-container").empty().hide();
            }
        });
    }

    _Class.prototype.set = function (value) {
        this.value = value;
    };

    return _Class;

})());
RemorqueurCaptain
la source
5

Quelque chose de très similaire peut être vu sur http://goo.gl lorsque vous raccourcissez l'URL.

Il y a un élément d'entrée en lecture seule qui se concentre sur le programme, avec une info-bulle CTRL-Cpour copier.

Lorsque vous appuyez sur ce raccourci, le contenu d'entrée entre effectivement dans le presse-papiers. Vraiment sympa :)

Boris Brdarić
la source