How does Trello access the user's clipboard?
When you hover over a card in Trello and press Ctrl+C, the URL of this card is copied to the clipboard. How do they do this?
As far as I can tell, there is no Flash movie involved. I've got Flashblock installed, and the Firefox network tab shows no Flash movie loaded. (That's the usual method, for example, by ZeroClipboard.)
How do they achieve this magic?
(Right at this moment I think I had an epiphany: You cannot select text on the page, so I assume they have an invisible element, where they create a text selection via JavaScript code, and Ctrl+C triggers the browser's default behaviour, copying that invisible node's text value.)
Disclosure: I wrote the code that Trello uses; the code below is the actual source code Trello uses to accomplish the clipboard trick.
We don't actually "access the user's clipboard", instead we help the user out a bit by selecting something useful when they press Ctrl+C.
Sounds like you've figured it out; we take advantage of the fact that when you want to hit Ctrl+C, you have to hit the Ctrl key first. When the Ctrl key is pressed, we pop in a textarea that contains the text we want to end up on the clipboard, and select all the text in it, so the selection is all set when the C key is hit. (Then we hide the textarea when the Ctrl key comes up)
Specifically, Trello does this:
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) ->
In the DOM we've got
<div id="clipboard-container"><textarea id="clipboard"></textarea></div>
CSS for the clipboard stuff:
#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;
}
... and the CSS makes it so you can't actually see the textarea when it pops in ... but it's "visible" enough to copy from.
When you hover over a card, it calls
TrelloClipboard.set(cardUrl)
... so then the clipboard helper knows what to select when the Ctrl key is pressed.
I actually built a Chrome extension that does exactly this, and for all web pages. The source code is on GitHub.
I find three bugs with Trello's approach, which I know because I've faced them myself :)
The copy doesn't work in these scenarios:
I solved #1 by always having a hidden span, rather than creating one when user hits Ctrl/Cmd.
I solved #2 by temporarily clearing the zero-length selection, saving the caret position, doing the copy and restoring the caret position.
I haven't found a fix for #3 yet :) (For information, check the open issue in my GitHub project).
With the help of raincoat's (link to GitHub) code, I managed to get a running version accessing the clipboard with plain 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");
The only problem is, that this version only works with Chrome. The Trello platform supports all browsers. What I am missing?
Sovled thanks to VadimIvanov.
See a working example: http://jsfiddle.net/AGEf7/
链接地址: http://www.djcxy.com/p/3320.html上一篇: 使用FireFox,Safari和Chrome在剪贴板上复制/放置文本
下一篇: Trello如何访问用户的剪贴板?