Bagaimana Trello mengakses clipboard pengguna?

936

Ketika Anda membawa kartu di Trello dan tekan Ctrl+C , URL kartu ini disalin ke clipboard. bagaimana mereka melakukan ini?

Sejauh yang saya tahu, tidak ada film Flash yang terlibat. Saya telah menginstal Flashblock , dan tab jaringan Firefox tidak menunjukkan film Flash dimuat. (Itu metode yang biasa, misalnya, oleh ZeroClipboard.)

Bagaimana mereka mencapai keajaiban ini?

(Tepat pada saat ini saya pikir saya memiliki epifani: Anda tidak dapat memilih teks pada halaman, jadi saya menganggap mereka memiliki elemen tak terlihat, di mana mereka membuat pemilihan teks melalui kode JavaScript, dan Ctrl+ Cmemicu perilaku default browser, menyalin yang tidak terlihat nilai teks simpul.)

Boldewyn
sumber
22
Jika Anda melihat DOM langsung, ada div dengan kelas "clipboard-container". Ketika Anda menahan tombol ctrl, itu akan diisi dengan textarea (dan dihapus ketika Anda mengangkat kunci ctrl). Saya akan menganggap pencerahan Anda benar. Saya hanya tidak yakin di mana mereka menyimpan URL per kartu
Ian
@Ian, ya, saya bisa konfirmasi, itulah cara kerjanya. Terima kasih telah menggali! (Saya tidak peduli dengan tempat URL disimpan. Saya tertarik pada teknologi clipboard-tanpa-flash.)
Boldewyn
2
Saya mencari profil Daniel, dan sepertinya, dia adalah pengembang Trello. (Aku bertanya-tanya, dari mana dia mendapatkan sumber Coffeescript.) Jadi dia memiliki keuntungan yang tidak adil ;-) Terima kasih!
Boldewyn
1
Saya tidak bermaksud mengurangi akal tentang teknik ini, cukup pintar; tapi saya tidak bisa tidak berpikir ini adalah, paling baik, dipublikasikan / didokumentasikan dengan buruk, dan paling buruk, pengalaman pengguna yang cukup menggelegar. Memang, ini tidak menggelisahkan secara invasif (karena saya tidak dapat mengingat waktu di mana saya secara tidak sengaja menyalin URL kartu), tetapi sebagai pengguna lama Trello saya sama sekali tidak tahu ini ada.
Michael Wales
3
@MichaelWales Fitur ini ditambahkan 5 hari yang lalu; kami masih mengujinya, dan jika tampaknya berfungsi itu akan didokumentasikan sebagai pintasan keyboard.
Daniel LeCheminant

Jawaban:

1547

Pengungkapan: Saya menulis kode yang digunakan Trello ; kode di bawah ini adalah kode sumber aktual yang digunakan Trello untuk menyelesaikan trik clipboard.


Kami sebenarnya tidak "mengakses clipboard pengguna", melainkan kami membantu pengguna keluar sedikit dengan memilih sesuatu yang bermanfaat ketika mereka menekan Ctrl+ C.

Kedengarannya seperti Anda sudah mengetahuinya; kami mengambil keuntungan dari kenyataan bahwa ketika Anda ingin menekan Ctrl+ C, Anda harus menekan Ctrltombol terlebih dahulu. Ketika Ctrltombol ditekan, kita memunculkan teks yang berisi teks yang ingin kita akhiri di clipboard, dan pilih semua teks di dalamnya, sehingga pemilihan semua diatur ketika tombol Cditekan. (Kemudian kita menyembunyikan textarea saat Ctrlkunci muncul)

Secara khusus, Trello melakukan ini:

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

Di DOM kita punya

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

CSS untuk hal-hal clipboard:

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

... dan CSS membuatnya sehingga Anda tidak bisa benar-benar melihat textarea ketika muncul ... tapi itu "terlihat" cukup untuk menyalin.

Ketika Anda mengarahkan kursor ke atas kartu, kartu itu memanggil

TrelloClipboard.set(cardUrl)

... jadi penolong clipboard tahu apa yang harus dipilih ketika Ctrltombol ditekan.

Daniel LeCheminant
sumber
3
Luar biasa! Tetapi bagaimana Anda memiliki Mac OS - apakah Anda "mendengarkan" tombol Command di sana?
Suman
28
Perlu dicatat bahwa metode yang sama juga berfungsi untuk menangkap konten yang dilekatkan
Michael Robinson
17
Ini kedengarannya buruk bagi pengguna keyboard - kapan pun Anda mencoba menyalin (atau ctrl + klik untuk membuka di jendela lain, atau Ctrl + F untuk mencari, dan sebagainya), fokus Anda dipindahkan ke suatu tempat yang tidak terkait.
Adam A
2
+1. Banyak hal rapi terjadi dalam jawaban ini. Saya suka bahwa Anda benar-benar membagikan kode sumber. Tapi yang saya pikir pintar adalah penjelasan sebenarnya dari proses yang digunakan untuk menyediakan fungsionalitas ctrl + c. Menurut pendapat saya itu sangat cerdas untuk mengambil keuntungan dari fakta bahwa ctrl dan c tidak dapat ditekan pada saat yang sama dengan mulai mempersiapkan c ketika ctrl ditekan. Saya sangat menyukai pendekatan itu.
Travis J
8
Jangan ragu untuk menggunakan js2coffee.org untuk menerjemahkan yang asli ke js jika diinginkan .
Alexandr Kurilin
79

Saya sebenarnya membangun ekstensi Chrome yang melakukan ini, dan untuk semua halaman web. Kode sumber ada di GitHub .

Saya menemukan tiga bug dengan pendekatan Trello, yang saya tahu karena saya sudah menghadapinya sendiri :)

Salinan tidak berfungsi dalam skenario ini:

  1. Jika Anda telah Ctrlmenekan dan kemudian mengarahkan tautan dan tekan C, salinan tidak berfungsi.
  2. Jika kursor Anda berada di bidang teks lain di halaman, salinannya tidak berfungsi.
  3. Jika kursor Anda ada di bilah alamat, salinannya tidak berfungsi.

Saya memecahkan # 1 dengan selalu memiliki rentang tersembunyi, daripada membuatnya ketika pengguna memukul Ctrl/ Cmd.

Saya memecahkan # 2 dengan menghapus sementara pilihan zero-length, menyimpan posisi caret, melakukan copy dan mengembalikan posisi caret.

Saya belum menemukan perbaikan untuk # 3 :) (Untuk informasi, periksa masalah terbuka di proyek GitHub saya).

Dhruv Vemula
sumber
10
Jadi Anda benar-benar melakukan ini dengan cara yang sama seperti Trello. Manis ketika hal-hal seperti itu bertemu
Thomas Ahle
@ Thomas, Apa maksudmu?
Pacerier
7
@ Peracerier, saya berasumsi bahwa Thomas menyinggung Konvergensi Evolusi - "... evolusi independen fitur serupa dalam spesies garis keturunan yang berbeda"
yoniLavi
sapi suci Anda bisa membuka obrolan baru tentang topik ini
carkod
20

Dengan bantuan kode ( tautan ke GitHub ) jas hujan , saya berhasil mendapatkan versi yang sedang berjalan mengakses clipboard dengan JavaScript biasa.

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

Satu-satunya masalah adalah, bahwa versi ini hanya berfungsi dengan Chrome. Platform Trello mendukung semua browser. Apa yang saya lewatkan?

Terimakasih berkat VadimIvanov.

Lihat contoh yang berfungsi: http://jsfiddle.net/AGEf7/

Felix
sumber
@ don41382 tidak berfungsi dengan baik di Safari (setidaknya versi Mac). Di bawah yang tepat maksud saya itu menyalin, tetapi Anda harus mendorong cmd + C dua kali.
Vadim Ivanov
@VadimIvanov Benar! Apakah ada yang tahu mengapa?
Felix
1
@ don41382 Saya tidak tahu persis mengapa, tetapi saya menemukan solusi. Anda memiliki bug minor, onKeyDown pernyataan pertama seharusnya if (! (E.ctrlKey || e.metaKey)) {return; } Ini berarti bahwa kita perlu menyiapkan textarea untuk salinan di metaKey ditekan (ini adalah cara orang-orang dari Trello membuat trik). Ini adalah kode dari trello.com gist.github.com/fustic/10870311
Vadim Ivanov
@VadimIvanov Terima kasih. Saya akan memperbaikinya di atas.
Felix
1
Itu tidak berfungsi di FF 33.1 karena el.innerTexttidak terdefinisi, jadi saya mengubah baris terakhir clipboard()fungsi menjadi clip.setValue(el.innerText || el.textContent);untuk kompatibilitas lintas-browser yang lebih banyak. link: jsfiddle.net/AGEf7/31
RevanProdigalKnight
7

Kode Daniel LeCheminant tidak berfungsi untuk saya setelah mengubahnya dari CoffeeScript ke JavaScript ( js2coffee ). Itu terus membom di _.defer()telepon.

Saya berasumsi ini ada hubungannya dengan jQuery yang ditangguhkan, jadi saya mengubahnya $.Deferred()dan berfungsi sekarang. Saya mengujinya di Internet Explorer 11, Firefox 35, dan Chrome 39 dengan jQuery 2.1.1. Penggunaannya sama seperti yang dijelaskan dalam posting 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;

})());
Tugboat Kapten
sumber
5

Sesuatu yang sangat mirip dapat dilihat di http://goo.gl ketika Anda mempersingkat URL.

Ada elemen input yang hanya bisa dibaca yang berfokus pada program, dengan tooltip tekan CTRL-Cuntuk menyalin.

Ketika Anda menekan pintasan itu, konten input secara efektif masuk ke clipboard. Benar-benar bagus :)

Boris Brdarić
sumber