In che modo Trello accede agli appunti dell'utente?


936

Quando passi il mouse su una carta in Trello e premi Ctrl+ C, l'URL di questa carta viene copiato negli appunti. Come lo fanno?

Per quanto ne so, non è coinvolto nessun filmato Flash. Ho Flashblock installato e la scheda di rete di Firefox non mostra alcun filmato Flash caricato. (Questo è il solito metodo, ad esempio, ZeroClipboard.)

Come ottengono questa magia?

(Proprio in questo momento penso di avere un'epifania: non è possibile selezionare il testo sulla pagina, quindi presumo che abbiano un elemento invisibile, in cui creano una selezione di testo tramite codice JavaScript e Ctrl+ Cattiva il comportamento predefinito del browser, copiando quello invisibile valore di testo del nodo).


22
Se guardi il DOM live, c'è un div con la classe "contenitore appunti". Quando si tiene premuto il tasto Ctrl, viene riempito con un'area di testo (e viene rimosso quando si solleva il tasto Ctrl). Suppongo che la tua epifania sia corretta. Non sono esattamente sicuro di dove stiano memorizzando l'URL per carta
Ian

@Ian, sì, posso confermare, è esattamente come ha funzionato. Grazie per averlo scavato! (Non mi preoccupo di dove sia archiviato l'URL. Ero interessato alla tecnologia appunti-senza-flash.)
Boldewyn

2
Ho cercato il profilo di Daniel, e sembra che sia uno sviluppatore di Trello. (Mi chiedevo da dove prendesse la fonte Coffeescript.) Quindi ha un vantaggio ingiusto ;-) Grazie comunque!
Boldewyn l'

1
Non intendo sminuire l'intraprendenza di questa tecnica, è abbastanza intelligente; ma non posso fare a meno di pensare che questa sia, nella migliore delle ipotesi, poco pubblicizzata / documentata e, nel peggiore dei casi, un'esperienza utente piuttosto sconcertante. Certo, non è invasivamente stonante (poiché non ricordo un momento in cui ho copiato accidentalmente l'URL della carta), ma come utente Trello di lunga data non avevo assolutamente idea che esistesse.
Michael Wales,

3
@MichaelWales Questa funzione è stata aggiunta 5 giorni fa; lo stiamo ancora testando e, se sembra funzionare, verrà documentato come scorciatoia da tastiera.
Daniel LeCheminant,

Risposte:


1547

Divulgazione: ho scritto il codice utilizzato da Trello ; il codice seguente è il codice sorgente effettivo che Trello usa per realizzare il trucco degli appunti.


In realtà non "accediamo agli appunti dell'utente", ma aiutiamo l'utente un po 'selezionando qualcosa di utile quando premono Ctrl+ C.

Sembra che tu l'abbia capito; sfruttiamo il fatto che quando vuoi premere Ctrl+ C, devi Ctrlprima premere il tasto. Quando Ctrlsi preme il tasto, pop-up in un'area di testo che contiene il testo che vogliamo finire negli Appunti e selezioniamo tutto il testo al suo interno, quindi la selezione viene impostata quando si Cpreme il tasto. (Quindi nascondiamo l'area di testo quando Ctrlviene visualizzata la chiave)

In particolare, Trello fa questo:

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

Nel DOM abbiamo

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

CSS per gli appunti:

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

... e il CSS lo rende in modo che tu non riesca effettivamente a vedere l'area di testo quando appare ... ma è "visibile" abbastanza da cui copiare.

Quando passi con il mouse su una carta, questa chiama

TrelloClipboard.set(cardUrl)

... quindi l'helper degli appunti sa cosa selezionare quando Ctrlviene premuto il tasto.


3
Eccezionale! Ma come hai Mac OS: ascolti il ​​tasto Comando lì?
Suman,

28
Vale la pena notare che un metodo simile funziona altrettanto bene per l'acquisizione di contenuti incollati
Michael Robinson,

17
Sembra che sarebbe male per gli utenti della tastiera - ogni volta che provi a copiare (o ctrl + clic per aprire in un'altra finestra, o Ctrl + F per cercare, e così via), la tua messa a fuoco viene spostata da qualche parte non correlata.
Adam A

2
+1. In questa risposta succedono molte cose interessanti. Mi piace che tu abbia effettivamente condiviso il codice sorgente. Ma ciò che pensavo fosse intelligente era la vera spiegazione del processo usato per fornire la funzionalità ctrl + c. A mio avviso, è stato molto intelligente sfruttare il fatto che ctrl e c non possono essere premuti contemporaneamente iniziando a prepararsi per il c quando si preme ctrl. Mi è piaciuto molto questo approccio.
Travis J,

8
Sentiti libero di usare js2coffee.org per tradurre l'originale in js se così incline.
Alexandr Kurilin,

79

In realtà ho creato un'estensione di Chrome che fa esattamente questo e per tutte le pagine web. Il codice sorgente è su GitHub .

Trovo tre bug con l'approccio di Trello, che conosco perché li ho affrontati io stesso :)

La copia non funziona in questi scenari:

  1. Se hai già Ctrlpremuto, quindi passa con il mouse su un collegamento e premi C, la copia non funziona.
  2. Se il cursore si trova in qualche altro campo di testo nella pagina, la copia non funziona.
  3. Se il cursore si trova nella barra degli indirizzi, la copia non funziona.

Ho risolto il primo problema avendo sempre un intervallo nascosto, anziché crearne uno quando l'utente preme Ctrl/ Cmd.

Ho risolto il problema n. 2 cancellando temporaneamente la selezione di lunghezza zero, salvando la posizione del cursore, facendo la copia e ripristinando la posizione del cursore.

Non ho ancora trovato una correzione per # 3 :) (Per informazioni, controlla il problema aperto nel mio progetto GitHub).


10
Quindi l'hai fatto allo stesso modo di Trello. Dolce quando convergono queste cose
Thomas Ahle

@ThomasAhle, che vuoi dire?
Pacerier,

7
@Pacerier, presumo che Thomas abbia alluso a Convergent Evolution - "... evoluzione indipendente di caratteristiche simili in specie di diversi lignaggi"
yoniLavi

mucca santa potresti aprire una nuova chat su questo argomento
carkod,

20

Con l'aiuto del codice di Raincoat ( link a GitHub ), sono riuscito a ottenere una versione funzionante accedendo agli Appunti con JavaScript semplice.

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

L'unico problema è che questa versione funziona solo con Chrome. La piattaforma Trello supporta tutti i browser. Cosa mi manca

Grazie grazie a VadimIvanov.

Vedi un esempio funzionante: http://jsfiddle.net/AGEf7/


@ don41382 non funziona correttamente su Safari (almeno versione Mac). Con il vero intendo che copia, ma devi premere due volte cmd + C.
Vadim Ivanov,

@VadimIvanov True! Qualcuno sa perché?
Felix,

1
@ don41382 Non so esattamente perché, ma ho trovato una soluzione. Hai un bug minore, onKeyDown la prima istruzione dovrebbe essere if (! (E.ctrlKey || e.metaKey)) {return; } Significa che dobbiamo preparare textarea per la copia su metaKey premuto (è così che i ragazzi di Trello hanno fatto un trucco). Questo è un codice di trello.com gist.github.com/fustic/10870311
Vadim Ivanov

@VadimIvanov Grazie. Lo riparerò sopra.
Felix,

1
Non funzionava in FF 33.1 perché el.innerTextnon era definito, quindi ho modificato l'ultima riga della clipboard()funzione in clip.setValue(el.innerText || el.textContent);per una maggiore compatibilità tra browser. link: jsfiddle.net/AGEf7/31
RevanProdigalKnight

7

Il codice di Daniel LeCheminant non ha funzionato per me dopo averlo convertito da CoffeeScript a JavaScript ( js2coffee ). Continuava a bombardare sulla _.defer()linea.

Ho pensato che questo avesse a che fare con i differiti di jQuery, quindi l'ho cambiato $.Deferred()e ora funziona. L'ho provato in Internet Explorer 11, Firefox 35 e Chrome 39 con jQuery 2.1.1. L'uso è lo stesso descritto nel post di 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;

})());

5

Qualcosa di molto simile può essere visto su http://goo.gl quando accorcia l'URL.

C'è un elemento di input di sola lettura che si concentra a livello di programmazione, con la descrizione del comando premere CTRL-Cper copiare.

Quando si preme quel collegamento, il contenuto di input viene effettivamente inserito negli Appunti. Veramente bello :)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.