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


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

Pour autant que je sache, il n'y a pas de film Flash impliqué. j'ai Flashblock installé, et l'onglet réseau Firefox ne montre aucun film Flash chargé. (C'est la méthode habituelle, par exemple, par ZeroClipboard.)

Comment réalisent-ils cette magie?

(En ce moment je pense que j'ai eu une révélation: Vous ne pouvez pas sélectionner le texte sur la page, alors je suppose qu'ils ont un élément invisible, où ils créent une sélection de texte via le 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.)


896
2017-07-08 13:26


origine


Réponses:


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.


En réalité, nous n'accédons pas au presse-papier de l'utilisateur, mais nous aidons 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 tu veux frapper Ctrl+C, vous devez frapper le Ctrl clé en premier. Quand le Ctrl touche est enfoncée, nous sautons dans une zone de texte qui contient le texte que nous voulons finir le presse-papiers, et sélectionnez tout le texte, donc la sélection est tous ensemble lorsque le C la touche est atteinte. (Ensuite, nous cachons la zone de texte lorsque le Ctrl clé arrive)

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 les DOM, nous avons

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

CSS pour les trucs du 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 le textarea quand il apparaît ... mais il est assez "visible" pour copier.

Lorsque vous passez la souris sur une carte, elle appelle

TrelloClipboard.set(cardUrl)

... alors l'assistant de presse-papiers sait ce qu'il faut sélectionner lorsque le Ctrl la touche est pressée.


1510
2017-07-08 14:00



J'ai effectivement construit une extension Chrome cela 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 pressé puis planer un lien et frapper 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 # 1 en ayant toujours une durée masquée, plutôt que d'en créer une lorsque l'utilisateur tape Ctrl/Cmd.

J'ai résolu # 2 en effaçant temporairement la sélection de longueur zéro, en sauvegardant la position du curseur, en faisant la copie et en rétablissant la position du curseur.

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


77
2017-08-03 23:01



Avec l'aide de l'imperméable (lien vers GitHub), j'ai réussi à obtenir une version en cours d'exécution en 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 fonctionne uniquement avec Chrome. La plateforme Trello prend en charge tous les navigateurs. Qu'est-ce qui me manque?

Sovled grâce à Vadim Ivanov.

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


19
2018-01-16 11:03



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

J'ai supposé que c'était quelque chose à voir avec jQuery différé, donc je l'ai changé pour $.Deferred() et ça marche 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 la publication 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
2018-01-18 00:22



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 readonly qui est focalisé par programme, avec une info-bulle "Appuyez sur CTRL-C pour copier". Lorsque vous cliquez sur ce raccourci, le contenu d'entrée entre effectivement dans le presse-papiers. Vraiment sympa :)


5
2017-08-05 20:32