web-dev-qa-db-fra.com

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

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

Autant que je sache, aucun film Flash n'est impliqué. J'ai Flashblock installé et l'onglet Réseau de Firefox ne montre aucune animation Flash chargée. (C'est la méthode habituelle, par exemple, avec ZeroClipboard.)

Comment réalisent-ils cette magie?

(En ce moment, je pense avoir eu une épiphanie: vous ne pouvez pas sélectionner du texte sur la page, alors je suppose qu'ils ont un élément invisible, où ils créent une sélection de texte via du code JavaScript, et Ctrl+C déclenche le comportement par défaut du navigateur, en copiant la valeur de texte de ce nœud invisible.)

920
Boldewyn

Divulgation: j'ai écrit le code que Trello utilise ; Le code ci-dessous est le code source utilisé par Trello pour accomplir l'astuce du presse-papier.


Nous n'avons pas réellement "accès au presse-papier de l'utilisateur", nous aidons plutôt l'utilisateur un peu en sélectionnant quelque chose d'utile quand il appuie sur Ctrl+C.

On dirait que vous l'avez compris. nous profitons du fait que lorsque vous voulez frapper Ctrl+C, vous devez frapper le Ctrl clé en premier. Quand le Ctrl la touche est enfoncée, nous entrons dans une zone de texte contenant le texte que nous voulons placer dans le presse-papiers et sélectionnons tout le texte qu’elle contient; la sélection est donc définie lorsque le C la clé est touchée. (Puis on cache la zone de texte quand le Ctrl la clé arrive)

Concrètement, 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 le rend si bien que vous ne pouvez pas voir la zone de texte quand elle apparaît ... mais elle est "suffisamment visible" pour pouvoir être copiée.

Lorsque vous survolez une carte, elle appelle

TrelloClipboard.set(cardUrl)

... Alors, l'assistant du presse-papiers sait quoi sélectionner lorsque le Ctrl la touche est enfoncée.

1534
Daniel LeCheminant

J'ai en fait construit ne 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 affrontés moi-même :)

La copie ne fonctionne pas dans ces scénarios:

  1. Si vous avez déjà Ctrl appuyé puis survolez un lien et appuyez sur 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 est dans la barre d'adresse, la copie ne fonctionne pas.

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

J'ai résolu le problème n ° 2 en effaçant temporairement la sélection de longueur zéro, en sauvegardant la position du curseur, en effectuant la copie et en restaurant la position du curseur.

Je n'ai pas encore trouvé de solution pour le n ° 3 :) (Pour plus d'informations, consultez la question ouverte dans mon projet GitHub).

79
Dhruv Vemula

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

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?

Merci à VadimIvanov.

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

20
Felix

Le code de Daniel LeCheminant ne fonctionnait pas pour moi après la conversion de CoffeeScript en JavaScript ( js2coffee ). Il continuait à bombarder la ligne _.defer().

J'ai supposé que cela avait quelque chose à voir avec jQuery différé, donc je l'ai changé en $.Deferred() et cela fonctionne maintenant. Je l'ai testé sous 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;

})());
7
TugboatCaptain

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

Il existe un élément d'entrée en lecture seule qui fait l'objet d'une programmation, avec l'info-bulle "Appuyez sur CTRL-C pour copier". Lorsque vous appuyez sur ce raccourci, le contenu saisi entre effectivement dans le Presse-papiers. Vraiment sympa :)

5
Boris Brdarić