Як Trello отримує доступ до буфера обміну користувача?


936

Якщо навести курсор миші на картку в Трелло та натиснути Ctrl+ C, URL-адреса цієї картки буде скопійована у буфер обміну. Як вони це роблять?

Наскільки я можу сказати, тут немає ніякого фільму Flash. У мене встановлено Flashblock , і на вкладці мережі Firefox показано, що фільм Flash не завантажений. (Це звичайний метод, наприклад, ZeroClipboard.)

Як вони досягають цієї магії?

(Якраз у цей момент я думаю, що у мене виникло епіфанію. Ви не можете вибрати текст на сторінці, тому я припускаю, що у них є невидимий елемент, де вони створюють виділення тексту за допомогою коду JavaScript і Ctrl+ Cзапускає поведінку браузера за замовчуванням, копіюючи це невидиме Текстове значення вузла.)


22
Якщо ви подивитеся на DOM в реальному часі, там є дів з класом "буфер обміну-контейнер". Утримуючи клавішу ctrl, вона заповнюється текстовою областю (і видаляється, коли ви знімаєте клавішу ctrl). Я вважаю, що ваше проголошення є правильним. Я просто не точно впевнений, де вони зберігають URL-адресу на карту
Ян

@Ian, так, можу підтвердити, саме так воно працювало. Дякуємо, що перекопали його! (Я не переймаюся тим, де зберігається URL-адреса. Мене зацікавив буфер обміну без спалаху.)
Boldewyn

2
Я подивився на профіль Даніеля, і, здається, він розробник Trello. (Я цікавився, звідки він узяв джерело Coffeescript.) Тож у нього несправедлива перевага ;-) Дякую все одно!
Boldewyn

1
Я не збираюся заважати винахідливості цієї методики, вона досить розумна; але я не можу не помітити, що це в кращому випадку погано розрекламована / задокументована, а в гіршому - досить привабливий досвід користувачів. Зрозуміло, це не інвазивно баламуче (оскільки я не можу пригадати час, коли я випадково скопіював URL-адресу картки), але, як давній користувач Trello, я абсолютно не мав ідеї, що це існувало.
Майкл Уельс

3
@MichaelWales Ця функція була додана 5 днів тому; ми все ще тестуємо його, і якщо здається, що це працює, це буде задокументовано як ярлик на клавіатурі.
Daniel LeCheminant

Відповіді:


1546

Розкриття інформації: я написав код, який використовує Trello ; наведений нижче код - це фактичний вихідний код, який Trello використовує для досягнення фокусу в буфері обміну.


Ми фактично не «отримуємо доступ до буфера обміну користувачами», натомість допомагаємо користувачеві трохи вибирати щось корисне, коли він натискає Ctrl+ C.

Здається, ви це зрозуміли; ми скористаємося тим, що коли ви хочете натиснути Ctrl+ C, ви повинні натиснути Ctrlклавішу спочатку. Коли Ctrlнатискається клавіша, ми вписуємо текстову область, яка містить текст, який ми хочемо в кінцевому підсумку, у буфер обміну, і вибираємо весь текст у ній, тому вибір встановлюється під час Cнатискання клавіші. (Потім ми ховаємо текстову область, коли з'являється Ctrlключ)

Конкретно, Trello робить це:

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

У DOM у нас є

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

CSS для буфера обміну:

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

... і CSS робить це таким чином, що ви фактично не можете бачити textarea, коли він з'являється ..., але це достатньо "видимо" для копіювання.

Коли ви наведіть курсор на карту, вона дзвонить

TrelloClipboard.set(cardUrl)

... тож помічник буфера обміну знає, що вибрати при Ctrlнатисканні клавіші.


3
Дивовижно! Але як у вас Mac OS - ви "слухаєте" там клавішу Command?
Суман

28
Варто зазначити, що подібний метод працює так само добре, як і для захоплення вставленого контенту
Майкл Робінсон

17
Це звучить так, що це буде погано для користувачів клавіатури - коли ви намагаєтесь скопіювати (або ctrl + натисніть, щоб відкрити в іншому вікні, або Ctrl + F для пошуку тощо), ваш фокус переміщується кудись не пов’язаним.
Адам А

2
+1. У цій відповіді багато акуратних речей. Мені подобається, що ви насправді поділилися вихідним кодом. Але те, що я вважав розумним, було власне поясненням процесу, який використовується для забезпечення функціональності ctrl + c. На мій погляд, було дуже розумно скористатися тим, що ctrl і c не можна натискати в той самий час, починаючи готуватися до c, коли ctrl натиснуто. Мені дуже сподобався такий підхід.
Тревіс J

8
Сміливо використовуйте js2coffee.org для перекладу оригіналу у js, якщо це так схильне.
Олександр Курилін

79

Я фактично створив розширення для Chrome, яке робить саме це, і для всіх веб-сторінок. Вихідний код знаходиться на GitHub .

Я знаходжу три помилки з підходом Трелло, яких я знаю, бо сам зіткнувся з ними :)

Копія не працює в таких сценаріях:

  1. Якщо ви вже Ctrlнатиснули, а потім наведіть на нього посилання та натисніть C, копія не працює.
  2. Якщо ваш курсор знаходиться в іншому текстовому полі сторінки, копія не працює.
  3. Якщо ваш курсор знаходиться в адресному рядку, копія не працює.

Я вирішив №1, завжди маючи прихований проміжок, а не створюючи його, коли користувач потрапляє Ctrl/ Cmd.

Я вирішив №2, тимчасово очистивши вибір нульової довжини, збереживши положення каретки, зробивши копію та відновивши положення каретки.

Я ще не знайшов виправлення №3 :) (Для інформації перевірте відкрите питання в моєму проекті GitHub).


10
Отже, ви насправді зробили це так само, як і Trello. Солодке, коли подібні речі сходяться
Томас Ейл

@ThomasAhle, що ти маєш на увазі?
Печер'є

7
@Pacerier, я припускаю, що Томас натякав на конвергентну еволюцію - "... незалежну еволюцію подібних особливостей у видів різних ліній"
yoniLavi

свята корова, ви можете відкрити новий чат на цю тему
carkod

20

За допомогою коду дощовика ( посилання на GitHub ) мені вдалося отримати запущену версію, що отримує доступ до буфера обміну за допомогою простого JavaScript.

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

Єдина проблема полягає в тому, що ця версія працює лише з Chrome. Платформа Trello підтримує всі браузери. Що мені не вистачає?

Вітаю завдяки Вадиму Іванову.

Дивіться робочий приклад: http://jsfiddle.net/AGEf7/


@ don41382 він не працює належним чином на Safari (принаймні версія для Mac). Під належним чином я маю на увазі, що вона копіює, але вам потрібно натиснути cmd + C двічі.
Вадим Іванов

@VadimIvanov Правда! Хтось знає, чому?
Фелікс

1
@ don41382 Не точно знаю чому, але я знайшов рішення. У вас є незначна помилка, onKeyDown перший вислів має бути if (! (E.ctrlKey || e.metaKey)) {return; } Це означає, що нам потрібно підготувати textarea для копіювання на metaKey натиснутою (саме так хлопці з trello зробили трюк). Це код з trello.com gist.github.com/fustic/10870311
Вадим Іванов

@VadimIvanov Дякую Я виправлю це вище.
Фелікс

1
Він не працював у FF 33.1, оскільки він не el.innerTextбув визначений, тому я змінив останній рядок clipboard()функції на clip.setValue(el.innerText || el.textContent);більшу сумісність між браузерами. посилання: jsfiddle.net/AGEf7/31
RevanProdigalKnight

7

Код Даніеля ЛеШемінанта не працював для мене після перетворення його з CoffeeScript в JavaScript ( js2coffee ). Це продовжувало бомбардувати на _.defer()лінії.

Я припускав, що це щось стосується відкладених jQuery, тому я змінив його на $.Deferred()і він працює зараз. Я перевірив його в Internet Explorer 11, Firefox 35 та Chrome 39 з jQuery 2.1.1. Використання таке саме, як описано в публікації Даниїла.

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

Щось дуже схоже можна побачити на http://goo.gl, коли ви скоротите URL-адресу.

Існує елемент для введення лише для читання, який фокусується у програмі, натискаючи підказку CTRL-Cдля копіювання.

При натисканні на цей ярлик вхідний вміст ефективно потрапляє в буфер обміну. Дійсно приємно :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.