Wie greift Trello auf die Zwischenablage des Benutzers zu?

936

Wenn Sie in Trello mit der Maus über eine Karte fahren und Ctrl+ drücken C, wird die URL dieser Karte in die Zwischenablage kopiert. Wie machen sie das?

Soweit ich das beurteilen kann, handelt es sich nicht um einen Flash-Film. Ich habe Flashblock installiert und auf der Registerkarte "Firefox-Netzwerk" wird kein Flash-Film geladen. (Dies ist die übliche Methode, zum Beispiel von ZeroClipboard.)

Wie erreichen sie diese Magie?

(Im Moment glaube ich, dass ich eine Offenbarung hatte: Sie können keinen Text auf der Seite auswählen, daher gehe ich davon aus, dass sie ein unsichtbares Element haben, in dem sie eine Textauswahl über JavaScript-Code erstellen, und Ctrl+ Cdas Standardverhalten des Browsers auslöst und diesen unsichtbaren kopiert Textwert des Knotens.)

Boldewyn
quelle
22
Wenn Sie sich das Live-DOM ansehen, gibt es ein Div mit der Klasse "Zwischenablage-Container". Wenn Sie die Strg-Taste gedrückt halten, wird sie mit einem Textbereich gefüllt (und wird entfernt, wenn Sie die Strg-Taste abheben). Ich würde annehmen, dass Ihre Offenbarung richtig ist. Ich bin mir nur nicht ganz sicher, wo sie die URL pro Karte speichern
Ian
@ Ian, ja, ich kann bestätigen, genau so hat es funktioniert. Danke, dass du es ausgegraben hast! (Ich
kümmere
2
Ich habe Daniels Profil nachgeschlagen und es scheint, dass er ein Trello-Entwickler ist. (Ich habe mich gefragt, woher er die Coffeescript-Quelle hat.) Also hat er einen ungerechten Vorteil ;-) Trotzdem danke!
Boldewyn
1
Ich habe nicht vor, den Einfallsreichtum dieser Technik zu beeinträchtigen, sie ist ziemlich klug. aber ich kann nicht anders, als zu denken, dass dies bestenfalls schlecht veröffentlicht / dokumentiert ist und im schlimmsten Fall eine ziemlich erschütternde Benutzererfahrung ist. Zugegeben, es ist nicht invasiv (da ich mich nicht an eine Zeit erinnern kann, in der ich versehentlich die Karten-URL kopiert habe), aber als langjähriger Trello-Benutzer hatte ich absolut keine Ahnung, dass dies existiert.
Michael Wales
3
@MichaelWales Diese Funktion wurde vor 5 Tagen hinzugefügt; Wir testen es noch und wenn es zu funktionieren scheint, wird es als Tastenkombination dokumentiert.
Daniel LeCheminant

Antworten:

1547

Offenlegung: Ich habe den Code geschrieben, den Trello verwendet . Der folgende Code ist der tatsächliche Quellcode, den Trello verwendet, um den Trick in der Zwischenablage auszuführen.


Wir "greifen nicht auf die Zwischenablage des Benutzers zu", sondern helfen dem Benutzer ein wenig, indem wir etwas Nützliches auswählen, wenn er Ctrl+ drückt C.

Klingt so, als hätten Sie es herausgefunden. Wir nutzen die Tatsache, dass Sie, wenn Sie Ctrl+ drücken möchten, zuerst Cdie CtrlTaste drücken müssen. Wenn die CtrlTaste gedrückt wird, wird ein Textbereich eingeblendet, der den Text enthält, der in der Zwischenablage angezeigt werden soll, und der gesamte darin enthaltene Text ausgewählt, sodass die Auswahl beim Drücken der CTaste festgelegt wird. (Dann verstecken wir den Textbereich, wenn der CtrlSchlüssel kommt)

Insbesondere tut Trello dies:

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

Im DOM haben wir

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

CSS für die Zwischenablage:

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

... und das CSS macht es so, dass Sie den Textbereich nicht sehen können, wenn er eingeblendet wird ... aber er ist "sichtbar" genug, um ihn zu kopieren.

Wenn Sie mit der Maus über eine Karte fahren, wird diese aufgerufen

TrelloClipboard.set(cardUrl)

... damit der Zwischenablage-Helfer weiß, was er auswählen soll, wenn die CtrlTaste gedrückt wird.

Daniel LeCheminant
quelle
3
Genial! Aber wie haben Sie Mac OS - hören Sie dort die Befehlstaste?
Suman
28
Es ist erwähnenswert, dass eine ähnliche Methode genauso gut zum Erfassen von eingefügten Inhalten funktioniert
Michael Robinson
17
Dies klingt so, als wäre es schlecht für Tastaturbenutzer. Jedes Mal, wenn Sie versuchen zu kopieren (oder Strg + Klicken, um es in einem anderen Fenster zu öffnen, oder Strg + F, um zu suchen usw.), wird Ihr Fokus an einen Ort verschoben, der nichts damit zu tun hat.
Adam A
2
+1. In dieser Antwort ist viel Ordentliches los. Ich finde es gut, dass Sie den Quellcode tatsächlich geteilt haben. Was ich jedoch für klug hielt, war die tatsächliche Erklärung des Prozesses, mit dem die Strg + C-Funktionalität bereitgestellt wurde. Meiner Meinung nach war es sehr klug, die Tatsache auszunutzen, dass Strg und C nicht genau gleichzeitig gedrückt werden können, indem man sich auf das C vorbereitet, wenn Strg gedrückt wird. Dieser Ansatz hat mir sehr gut gefallen.
Travis J
8
Fühlen Sie sich frei, js2coffee.org zu verwenden, um das Original in js zu übersetzen, wenn Sie dazu neigen.
Alexandr Kurilin
79

Ich habe tatsächlich eine Chrome-Erweiterung erstellt , die genau dies und für alle Webseiten tut. Der Quellcode ist auf GitHub .

Ich finde drei Fehler bei Trellos Ansatz, die ich kenne, weil ich mich ihnen selbst gestellt habe :)

Die Kopie funktioniert in folgenden Szenarien nicht:

  1. Wenn Sie bereits Ctrlgedrückt haben und dann einen Link bewegen und drücken C, funktioniert die Kopie nicht.
  2. Befindet sich Ihr Cursor in einem anderen Textfeld auf der Seite, funktioniert die Kopie nicht.
  3. Befindet sich Ihr Cursor in der Adressleiste, funktioniert die Kopie nicht.

Ich habe # 1 gelöst, indem ich immer eine versteckte Spanne hatte, anstatt eine zu erstellen, wenn der Benutzer Ctrl/ trifft Cmd.

Ich löste # 2, indem ich vorübergehend die Auswahl der Länge Null löschte, die Caret-Position speicherte, die Kopie machte und die Caret-Position wiederherstellte.

Ich habe noch keinen Fix für # 3 gefunden :) (Weitere Informationen finden Sie im offenen Problem in meinem GitHub-Projekt.)

Dhruv Vemula
quelle
10
Sie haben das also genauso gemacht wie Trello. Süß, wenn solche Dinge zusammenlaufen
Thomas Ahle
@ ThomasAhle, was meinst du?
Pacerier
7
@ Pacerier, ich gehe davon aus, dass Thomas auf Convergent Evolution anspielte - "... unabhängige Evolution ähnlicher Merkmale bei Arten verschiedener Abstammungslinien"
yoniLavi
Heilige Kuh, Sie könnten einen neuen Chat über dieses Thema
eröffnen
20

Mit Hilfe des Codes von raincoat ( Link zu GitHub ) gelang es mir, eine laufende Version zu erhalten, die mit einfachem JavaScript auf die Zwischenablage zugreift.

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

Das einzige Problem ist, dass diese Version nur mit Chrome funktioniert. Die Trello-Plattform unterstützt alle Browser. Was fehlt mir?

Dank VadimIvanov.

Siehe ein Arbeitsbeispiel: http://jsfiddle.net/AGEf7/

Felix
quelle
@ don41382 es funktioniert nicht richtig auf Safari (zumindest Mac-Version). Unter richtig meine ich, es kopiert, aber Sie müssen cmd + C zweimal drücken.
Vadim Ivanov
@VadimIvanov Richtig! Weiß jemand warum?
Felix
1
@ don41382 Ich weiß nicht genau warum, aber ich habe eine Lösung gefunden. Sie haben einen kleinen Fehler, onKeyDown sollte die erste Anweisung lauten, wenn (! (E.ctrlKey || e.metaKey)) {return; } Es bedeutet, dass wir den Textbereich für das Kopieren auf metaKey press vorbereiten müssen (so haben die Jungs von trello einen Trick gemacht). Dies ist ein Code von trello.com gist.github.com/fustic/10870311
Vadim Ivanov
@VadimIvanov Danke. Ich werde es oben beheben.
Felix
1
In FF 33.1 funktionierte el.innerTextes nicht, da es undefiniert war. Daher habe ich die letzte Zeile der clipboard()Funktion geändert, um die clip.setValue(el.innerText || el.textContent);browserübergreifende Kompatibilität zu verbessern. Link: jsfiddle.net/AGEf7/31
RevanProdigalKnight
7

Daniel LeCheminants Code funktionierte bei mir nicht, nachdem er von CoffeeScript in JavaScript ( js2coffee ) konvertiert wurde . Es bombardierte immer wieder auf der _.defer()Linie.

Ich nahm an, dass dies etwas mit jQuery-Verzögerungen zu tun hat, also habe ich es geändert $.Deferred()und es funktioniert jetzt. Ich habe es in Internet Explorer 11, Firefox 35 und Chrome 39 mit jQuery 2.1.1 getestet. Die Verwendung ist die gleiche wie in Daniels Beitrag beschrieben.

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;

})());
Schlepper-Kapitän
quelle
5

Etwas sehr Ähnliches ist auf http://goo.gl zu sehen, wenn Sie die URL kürzen.

Es gibt ein schreibgeschütztes Eingabeelement, das programmgesteuert fokussiert wird, wobei die QuickInfo CTRL-Czum Kopieren gedrückt wird .

Wenn Sie diese Verknüpfung drücken, wird der Eingabeinhalt effektiv in die Zwischenablage verschoben. Wirklich nett :)

Boris Brdarić
quelle