Làm thế nào để Trello truy cập vào clipboard của người dùng?


936

Khi bạn di chuột qua thẻ trong Trello và nhấn Ctrl+ C, URL của thẻ này sẽ được sao chép vào bảng tạm. Làm thế nào để họ làm điều này?

Theo như tôi có thể nói, không có phim Flash nào liên quan. Tôi đã cài đặt Flashblock và tab mạng Firefox hiển thị không tải phim Flash. (Đó là phương pháp thông thường, ví dụ, bằng ZeroClipboard.)

Làm thế nào để họ đạt được điều kỳ diệu này?

(Ngay tại thời điểm này tôi nghĩ rằng tôi đã có một sự hiển linh: Bạn không thể chọn văn bản trên trang, vì vậy tôi cho rằng họ có một yếu tố vô hình, nơi mà họ tạo ra một lựa chọn văn bản thông qua mã JavaScript, và Ctrl+ Ctrigger hành vi mặc định của trình duyệt, sao chép mà vô hình giá trị văn bản của nút.)


22
Nếu bạn nhìn vào DOM trực tiếp, sẽ có một div với lớp "clipboard-container". Khi bạn giữ phím ctrl, nó sẽ được lấp đầy bằng một textarea (và bị xóa khi bạn nhấc phím ctrl). Tôi sẽ cho rằng epiphany của bạn là chính xác. Tôi chỉ không chắc chắn chính xác nơi họ đang lưu trữ URL trên mỗi thẻ
Ian

@Ian, vâng, tôi có thể xác nhận, đó chính xác là cách nó hoạt động. Cảm ơn đã đào nó lên! (Tôi không bận tâm về nơi URL được lưu trữ. Tôi quan tâm đến công nghệ clipboard mà không cần flash.)
Boldewyn

2
Tôi đã tra cứu hồ sơ của Daniel và dường như, anh ấy là một nhà phát triển Trello. (Tôi tự hỏi, anh ta lấy nguồn Coffeescript từ đâu.) Vì vậy, anh ta có một lợi thế bất công ;-) Dù sao cũng cảm ơn!
Boldewyn

1
Tôi không có ý định làm giảm sự tháo vát của kỹ thuật này, nó khá thông minh; nhưng tôi không thể không nghĩ rằng, tốt nhất là, công khai / tài liệu kém, và tồi tệ nhất, một trải nghiệm người dùng khá chói tai. Được cho phép, nó không bị xâm phạm một cách xâm lấn (vì tôi không thể nhớ lại thời gian tôi vô tình sao chép URL thẻ), nhưng là một người dùng Trello lâu năm, tôi hoàn toàn không biết điều này tồn tại.
Michael Wales

3
@MichaelWales Tính năng này đã được thêm 5 ngày trước; chúng tôi vẫn đang thử nghiệm nó và nếu nó dường như đang hoạt động thì nó sẽ được ghi lại dưới dạng phím tắt.
Daniel LeCheminant

Câu trả lời:


1547

Tiết lộ: Tôi đã viết mã mà Trello sử dụng ; mã dưới đây là mã nguồn thực tế mà Trello sử dụng để thực hiện thủ thuật clipboard.


Chúng tôi thực sự không "truy cập vào bảng tạm của người dùng", thay vào đó chúng tôi giúp người dùng thoát ra một chút bằng cách chọn thứ gì đó hữu ích khi họ nhấn Ctrl+ C.

Âm thanh như bạn đã tìm ra nó; chúng tôi tận dụng thực tế là khi bạn muốn nhấn Ctrl+ C, bạn phải nhấn Ctrlphím trước. Khi Ctrlnhấn phím, chúng tôi bật trong một vùng văn bản có chứa văn bản mà chúng tôi muốn kết thúc trên bảng tạm và chọn tất cả văn bản trong đó, vì vậy tất cả các lựa chọn được đặt khi Cnhấn phím. (Sau đó, chúng tôi ẩn textarea khi Ctrlkhóa xuất hiện)

Cụ thể, Trello thực hiện điều này:

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

Trong DOM chúng tôi đã có

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

CSS cho nội dung 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;
}

... và CSS làm cho nó để bạn thực sự không thể nhìn thấy vùng văn bản khi nó xuất hiện ... nhưng nó "hiển thị" đủ để sao chép từ đó.

Khi bạn di chuột qua thẻ, nó gọi

TrelloClipboard.set(cardUrl)

... Vì vậy, sau đó trình trợ giúp clipboard biết những gì cần chọn khi Ctrlnhấn phím.


3
Tuyệt vời! Nhưng làm thế nào để bạn có Mac OS - bạn có "nghe" phím Command ở đó không?
Suman

28
Điều đáng chú ý là một phương pháp tương tự cũng hoạt động tốt để ghi lại nội dung được dán
Michael Robinson

17
Điều này nghe có vẻ tệ cho người dùng bàn phím - bất cứ khi nào bạn cố gắng sao chép (hoặc ctrl + nhấp để mở trong cửa sổ khác hoặc Ctrl + F để tìm kiếm, v.v.), trọng tâm của bạn được chuyển đến một nơi không liên quan.
Adam A

2
+1. Rất nhiều thứ gọn gàng đang diễn ra trong câu trả lời này. Tôi thích rằng bạn thực sự chia sẻ mã nguồn. Nhưng điều tôi nghĩ là thông minh là lời giải thích thực tế về quy trình được sử dụng để cung cấp chức năng ctrl + c. Theo tôi, rất thông minh khi tận dụng thực tế là không thể nhấn ctrl và c cùng một lúc bằng cách bắt đầu chuẩn bị cho c khi đẩy ctrl. Tôi thực sự thích cách tiếp cận đó.
Travis J

8
Vui lòng sử dụng js2coffee.org để dịch bản gốc sang js nếu có xu hướng.
Alexandr Kurilin

79

Tôi thực sự đã xây dựng một tiện ích mở rộng Chrome thực hiện chính xác điều này và cho tất cả các trang web. Mã nguồn là trên GitHub .

Tôi tìm thấy ba lỗi với cách tiếp cận của Trello, mà tôi biết vì tôi đã tự mình đối mặt với chúng :)

Bản sao không hoạt động trong các tình huống sau:

  1. Nếu bạn đã Ctrlnhấn và sau đó di chuột một liên kết và nhấn C, bản sao không hoạt động.
  2. Nếu con trỏ của bạn ở trong một số trường văn bản khác trong trang, bản sao sẽ không hoạt động.
  3. Nếu con trỏ của bạn nằm trong thanh địa chỉ, bản sao không hoạt động.

Tôi đã giải quyết # 1 bằng cách luôn có một khoảng ẩn, thay vì tạo một khoảng khi người dùng nhấn Ctrl/ Cmd.

Tôi đã giải quyết # 2 bằng cách tạm thời xóa lựa chọn có độ dài bằng không, lưu vị trí dấu mũ, thực hiện sao chép và khôi phục vị trí dấu mũ.

Tôi chưa tìm thấy bản sửa lỗi nào cho số 3 :) (Để biết thông tin, hãy kiểm tra vấn đề mở trong dự án GitHub của tôi).


10
Vì vậy, bạn thực sự đã làm điều này giống như Trello. Thật ngọt ngào khi những điều như vậy hội tụ
Thomas Ahle

@ThomasAhle, ý bạn là gì?
Pacerier

7
@Pacerier, tôi cho rằng Thomas đã ám chỉ sự tiến hóa hội tụ - "... sự tiến hóa độc lập của các tính năng tương tự trong các loài có dòng dõi khác nhau"
yoniLavi

bò thần bạn có thể mở một cuộc trò chuyện mới về chủ đề này
carkod

20

Với sự trợ giúp của mã áo mưa ( liên kết đến GitHub ), tôi đã quản lý để có được một phiên bản đang chạy truy cập vào bảng tạm với JavaScript đơn giản.

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

Vấn đề duy nhất là, phiên bản này chỉ hoạt động với Chrome. Nền tảng Trello hỗ trợ tất cả các trình duyệt. Tôi đang thiếu gì?

Cảm ơn rất nhiều nhờ có VadimIvanov.

Xem một ví dụ hoạt động: http://jsfiddle.net/AGEf7/


@ don41382 nó không hoạt động đúng trên Safari (ít nhất là phiên bản Mac). Theo đúng nghĩa tôi có nghĩa là nó sao chép, nhưng bạn phải đẩy cmd + C hai lần.
Vadim Ivanov

@VadimIvanov Đúng! Có ai biết tại sao không?
Felix

1
@ don41382 Tôi không biết chính xác tại sao, nhưng tôi đã tìm thấy một giải pháp. Bạn có một lỗi nhỏ, onKeyDown câu lệnh đầu tiên phải là if (! (E.ctrlKey || e.metaKey)) {return; } Điều đó có nghĩa là chúng ta cần chuẩn bị textarea để sao chép trên metaKey được nhấn (đây là cách những kẻ từ trello thực hiện một mánh khóe). Đây là mã từ trello.com gist.github.com/fustic/10870311
Vadim Ivanov

@VadimIvanov Cảm ơn. Tôi sẽ sửa nó ở trên.
Felix

1
Nó không hoạt động trong FF 33.1 vì el.innerTextkhông được xác định, vì vậy tôi đã thay đổi dòng cuối cùng của clipboard()chức năng để clip.setValue(el.innerText || el.textContent);tương thích nhiều trình duyệt hơn. link: jsfiddle.net/AGEf7/31
RevanProdigalKnight

7

Mã của Daniel LeCheminant không hoạt động với tôi sau khi chuyển đổi nó từ CoffeeScript sang JavaScript ( js2coffee ). Nó tiếp tục ném bom trên _.defer()đường dây.

Tôi giả định rằng đây là một cái gì đó để làm với việc trì hoãn jQuery, vì vậy tôi đã thay đổi nó $.Deferred()và nó đang hoạt động. Tôi đã thử nghiệm nó trong Internet Explorer 11, Firefox 35 và Chrome 39 với jQuery 2.1.1. Cách sử dụng giống như được mô tả trong bài viết của 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

Một cái gì đó rất giống nhau có thể được nhìn thấy trên http://goo.gl khi bạn rút ngắn URL.

Có một yếu tố đầu vào chỉ đọc được tập trung theo chương trình, với nhấn công cụ CTRL-Cđể sao chép.

Khi bạn nhấn phím tắt đó, nội dung đầu vào sẽ được đưa vào bảng tạm. Thực sự tốt đẹp :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.