Pregunta ¿Cómo accede Trello al portapapeles del usuario?


Cuando pasa el mouse sobre una carta en Trello y prensa Ctrl+do, la URL de esta tarjeta se copia en el portapapeles. ¿Cómo lo hacen?

Por lo que puedo decir, no hay película Flash involucrada. Tengo Flashblock instalado, y la pestaña de red de Firefox no muestra ninguna película Flash cargada. (Ese es el método habitual, por ejemplo, de ZeroClipboard).

¿Cómo logran esta magia?

(Justo en este momento, creo que tuve una epifanía: no se puede seleccionar texto en la página, así que supongo que tienen un elemento invisible, donde crean una selección de texto mediante código JavaScript, y Ctrl+do desencadena el comportamiento predeterminado del navegador, copiando el valor de texto del nodo invisible).


896
2017-07-08 13:26


origen


Respuestas:


Revelación:  Escribí el código que usa Trello; el siguiente código es el código fuente real que Trello usa para realizar el truco del portapapeles.


En realidad, no "accedemos al portapapeles del usuario", sino que ayudamos al usuario un poco seleccionando algo útil cuando presionan Ctrl+do.

Parece que lo has descubierto; aprovechamos el hecho de que cuando quieres golpear Ctrl+do, tienes que golpear Ctrl llave primero. Cuando el Ctrl se presiona la tecla, se abre un área de texto que contiene el texto que queremos que termine en el portapapeles, y seleccionamos todo el texto en él, de modo que la selección se establece cuando el do la clave está golpeada. (Entonces ocultamos el área de texto cuando el Ctrl la clave aparece)

Específicamente, Trello hace esto:

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) ->

En el DOM tenemos

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

CSS para las cosas del portapapeles:

#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;
}

... y el CSS lo hace para que no puedas ver el área de texto cuando aparece ... pero es lo suficientemente "visible" para copiar.

Cuando pasa el mouse sobre una carta, llama

TrelloClipboard.set(cardUrl)

... entonces el ayudante del portapapeles sabe qué seleccionar cuando Ctrl la tecla está presionada


1510
2017-07-08 14:00



Yo realmente construí una extensión de Chrome que hace exactamente esto, y para todas las páginas web. El código fuente es en GitHub.

Encuentro tres errores con el enfoque de Trello, que sé porque los he enfrentado a mí mismo :)

La copia no funciona en estos escenarios:

  1. Si ya tienes Ctrl presionada y luego coloque un enlace y golpee do, la copia no funciona.
  2. Si su cursor está en algún otro campo de texto en la página, la copia no funciona.
  3. Si el cursor está en la barra de direcciones, la copia no funciona.

Resolví el número 1 teniendo siempre un intervalo oculto, en lugar de crear uno cuando el usuario acierta Ctrl/Cmd.

Resolví el n. ° 2 borrando temporalmente la selección de longitud cero, guardando la posición de intercalación, haciendo la copia y restaurando la posición de intercalación.

Todavía no he encontrado una solución para el n. ° 3 :) (Para información, verifique el problema abierto en mi proyecto GitHub).


77
2017-08-03 23:01



Con la ayuda de impermeables (enlace a GitHub), logré obtener una versión en ejecución para acceder al portapapeles con 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");

El único problema es que esta versión solo funciona con Chrome. La plataforma Trello es compatible con todos los navegadores. Lo que me estoy perdiendo?

Sovled gracias a VadimIvanov.

Vea un ejemplo de trabajo: http://jsfiddle.net/AGEf7/


19
2018-01-16 11:03



El código de Daniel LeCheminant no funcionó para mí después de convertirlo de CoffeeScript a JavaScript (js2coffee) Siguió bombardeando en el _.defer() línea.

Supuse que esto era algo relacionado con jQuery diferido, así que lo cambié a $.Deferred() y está funcionando ahora. Lo probé en Internet Explorer 11, Firefox 35 y Chrome 39 con jQuery 2.1.1. El uso es el mismo que se describe en la publicación 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



Algo muy similar se puede ver en http://goo.gl cuando acorta la URL.

Hay un elemento de entrada de solo lectura que se enfoca programáticamente, con la información sobre herramientas "Presione CTRL-C para copiar". Cuando tocas ese atajo, el contenido de entrada entra efectivamente en el portapapeles. Muy agradable :)


5
2017-08-05 20:32