Скопировать в буфер обмена с помощью Javascript в iOS
Я использую эту функцию для копирования URL-адреса в буфер обмена:
function CopyUrl($this){
var querySelector = $this.next().attr("id");
var emailLink = document.querySelector("#"+querySelector);
var range = document.createRange();
range.selectNode(emailLink);
window.getSelection().addRange(range);
try {
// Now that we've selected the anchor text, execute the copy command
var successful = document.execCommand('copy', false, null);
var msg = successful ? 'successful' : 'unsuccessful';
if(true){
$this.addClass("copied").html("Copied");
}
} catch(err) {
console.log('Oops, unable to copy');
}
// Remove the selections - NOTE: Should use
// removeRange(range) when it is supported
window.getSelection().removeAllRanges();
}
Все отлично работает на настольных браузерах, но не на устройствах iOS, где моя функция успешно возвращается, но данные не копируются в буфер обмена вообще. Что вызывает это и как я могу решить эту проблему?
Ответы
Ответ 1
Обновить! iOS> = 10
Похоже, с помощью диапазонов выделения и небольшого взлома можно напрямую скопировать в буфер обмена на iOS (> = 10) Safari. Я лично проверял это на iPhone 5C iOS 10.3.3 и iPhone 8 iOS 11.1. Тем не менее, существуют некоторые ограничения:
- Текст может быть скопирован только из элементов
<input>
и <textarea>
.
- Если элемент, содержащий текст, не находится внутри
<form>
, то он должен быть contenteditable
.
- Элемент, содержащий текст, не должен быть
readonly
(хотя вы можете попробовать, это не "официальный" метод, описанный где-либо).
- Текст внутри элемента должен находиться в диапазоне выделения.
Чтобы покрыть все эти четыре "требования", вам необходимо:
- Поместите текст для копирования в элемент
<input>
или <textarea>
.
- Сохраните старые значения
contenteditable
и readonly
элемента, чтобы иметь возможность восстановить их после копирования.
- Измените
contenteditable
на true
и readonly
на false
.
- Создайте диапазон, чтобы выбрать нужный элемент и добавить его в окно выбора.
- Установите диапазон выбора для всего элемента.
- Восстановите предыдущие значения
contenteditable
и readonly
.
- Запустите
execCommand('copy')
.
Это приведет к перемещению каретки пользовательского устройства и выделению всего текста в требуемом элементе, а затем автоматически выполнит команду копирования. Пользователь увидит выбранный текст и подсказку с параметрами select/copy/paste.
Теперь это выглядит немного сложнее и слишком хлопотно, чтобы просто выполнить команду копирования, поэтому я не уверен, что это был намеренный выбор дизайна Apple, но кто знает... в то же время, это в настоящее время работает на iOS> = 10.
С учетом вышесказанного, полифилы, такие как и, можно использовать для упрощения этого действия и обеспечения его совместимости с различными браузерами (спасибо @Toskan за ссылку в комментариях).
Рабочий пример
Подводя итог, код, который вам нужен, выглядит следующим образом:
function iosCopyToClipboard(el) {
var oldContentEditable = el.contentEditable,
oldReadOnly = el.readOnly,
range = document.createRange();
el.contentEditable = true;
el.readOnly = false;
range.selectNodeContents(el);
var s = window.getSelection();
s.removeAllRanges();
s.addRange(range);
el.setSelectionRange(0, 999999); // A big number, to cover anything that could be inside the element.
el.contentEditable = oldContentEditable;
el.readOnly = oldReadOnly;
document.execCommand('copy');
}
Обратите внимание, что параметр el
для этой функции должен быть <input>
или <textarea>
.
Старый ответ: предыдущие версии iOS
На iOS & lt; 10 существуют некоторые ограничения для Safari (которые фактически являются мерами безопасности) для буфера обмена API:
- Он запускает события
copy
только в допустимом выделении, а cut
и paste
только в сфокусированных редактируемых полях.
- Он поддерживает только чтение/запись в буфер обмена ОС с помощью сочетаний клавиш, а не с помощью
document.execCommand()
. Обратите внимание, что "клавиша быстрого доступа" означает некоторые интерактивные (например, меню действий копирования/вставки или пользовательские сочетания клавиш iOS) или физическую клавишу (например, подключенный Bluetooth). клавиатуры).
- Он не поддерживает конструктор
ClipboardEvent
.
Поэтому (по крайней мере, на данный момент) невозможно программно скопировать некоторый текст/значение в буфер обмена на устройстве iOS с использованием Javascript. Только пользователь может решить, копировать ли что-либо.
Однако можно выбрать что-то программно, так что пользователю нужно только нажать всплывающую подсказку "Копировать", показанную на выделении. Это может быть достигнуто с помощью того же кода, что и выше, просто удалив execCommand('copy')
, который действительно не будет работать.
Ответ 2
Я искал некоторые решения, и я нашел тот, который действительно работает: http://www.seabreezecomputers.com/tips/copy2clipboard.htm
В принципе, примером может быть что-то вроде:
var $input = $(' some input/textarea ');
$input.val(result);
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
var el = $input.get(0);
var editable = el.contentEditable;
var readOnly = el.readOnly;
el.contentEditable = true;
el.readOnly = false;
var range = document.createRange();
range.selectNodeContents(el);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
el.setSelectionRange(0, 999999);
el.contentEditable = editable;
el.readOnly = readOnly;
} else {
$input.select();
}
document.execCommand('copy');
$input.blur();
Ответ 3
Проблема: iOS Safari разрешает document.execCommand('copy')
только текст в контейнере contentEditable
.
Решение: обнаружить iOS Safari и быстро переключить contentEditable
перед выполнением document.execCommand('copy')
.
Функция ниже работает во всех браузерах. Звоните с помощью CSS Selector или HTMLElement:
function copyToClipboard(el) {
// resolve the element
el = (typeof el === 'string') ? document.querySelector(el) : el;
// handle iOS as a special case
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
// save current contentEditable/readOnly status
var editable = el.contentEditable;
var readOnly = el.readOnly;
// convert to editable with readonly to stop iOS keyboard opening
el.contentEditable = true;
el.readOnly = true;
// create a selectable range
var range = document.createRange();
range.selectNodeContents(el);
// select the range
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
el.setSelectionRange(0, 999999);
// restore contentEditable/readOnly to original state
el.contentEditable = editable;
el.readOnly = readOnly;
}
else {
el.select();
}
// execute copy command
document.execCommand('copy');
}
input { font-size: 14px; font-family: tahoma; }
button { font-size: 14px; font-family: tahoma; }
<input class="important-message" type="text" value="Hello World" />
<button onclick="copyToClipboard('.important-message')">Copy</button>
Ответ 4
Это моя кросс-браузерная реализация
Вы можете проверить это, запустив фрагмент ниже
Пример:
copyToClipboard("Hello World");
/**
* Copy a string to clipboard
* @param {String} string The string to be copied to clipboard
* @return {Boolean} returns a boolean correspondent to the success of the copy operation.
*/
function copyToClipboard(string) {
let textarea;
let result;
try {
textarea = document.createElement('textarea');
textarea.setAttribute('readonly', true);
textarea.setAttribute('contenteditable', true);
textarea.style.position = 'fixed'; // prevent scroll from jumping to the bottom when focus is set.
textarea.value = string;
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
const range = document.createRange();
range.selectNodeContents(textarea);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
textarea.setSelectionRange(0, textarea.value.length);
result = document.execCommand('copy');
} catch (err) {
console.error(err);
result = null;
} finally {
document.body.removeChild(textarea);
}
// manual copy fallback using prompt
if (!result) {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
const copyHotkey = isMac ? '⌘C' : 'CTRL+C';
result = prompt('Press ${copyHotkey}', string); // eslint-disable-line no-alert
if (!result) {
return false;
}
}
return true;
}
Demo: <button onclick="copyToClipboard('It works!\nYou can upvote my answer now :)') ? this.innerText='Copied!': this.innerText='Sorry :(' ">Click here</button>
<p>
<textarea placeholder="(Testing area) Paste here..." cols="80" rows="4"></textarea>
</p>
Ответ 5
Пожалуйста, проверьте мое решение.
Он работает в Safari (проверено на iPhone 7 и iPad) и в других браузерах.
window.Clipboard = (function(window, document, navigator) {
var textArea,
copy;
function isOS() {
return navigator.userAgent.match(/ipad|iphone/i);
}
function createTextArea(text) {
textArea = document.createElement('textArea');
textArea.value = text;
document.body.appendChild(textArea);
}
function selectText() {
var range,
selection;
if (isOS()) {
range = document.createRange();
range.selectNodeContents(textArea);
selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
textArea.setSelectionRange(0, 999999);
} else {
textArea.select();
}
}
function copyToClipboard() {
document.execCommand('copy');
document.body.removeChild(textArea);
}
copy = function(text) {
createTextArea(text);
selectText();
copyToClipboard();
};
return {
copy: copy
};
})(window, document, navigator);
// How to use
Clipboard.copy('text to be copied');
https://gist.github.com/rproenca/64781c6a1329b48a455b645d361a9aa3 https://fiddle.jshell.net/k9ejqmqt/1/
Надеюсь, это поможет вам.
С уважением.
Ответ 6
Мое решение было создано путем объединения других ответов с этой страницы.
В отличие от других ответов, это не требует, чтобы у вас уже был элемент на странице. Он создаст свою собственную текстовую область и впоследствии уберет беспорядок.
function copyToClipboard(str) {
var el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style = {position: 'absolute', left: '-9999px'};
document.body.appendChild(el);
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
// save current contentEditable/readOnly status
var editable = el.contentEditable;
var readOnly = el.readOnly;
// convert to editable with readonly to stop iOS keyboard opening
el.contentEditable = true;
el.readOnly = true;
// create a selectable range
var range = document.createRange();
range.selectNodeContents(el);
// select the range
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
el.setSelectionRange(0, 999999);
// restore contentEditable/readOnly to original state
el.contentEditable = editable;
el.readOnly = readOnly;
} else {
el.select();
}
document.execCommand('copy');
document.body.removeChild(el);
}
Ответ 7
хороший вариант, здесь приведен вышеописанный рефакторинг, если кому-то это интересно (написано как модуль ES6):
type EditableInput = HTMLTextAreaElement | HTMLInputElement;
const selectText = (editableEl: EditableInput, selectionStart: number, selectionEnd: number) => {
const isIOS = navigator.userAgent.match(/ipad|ipod|iphone/i);
if (isIOS) {
const range = document.createRange();
range.selectNodeContents(editableEl);
const selection = window.getSelection(); // current text selection
selection.removeAllRanges();
selection.addRange(range);
editableEl.setSelectionRange(selectionStart, selectionEnd);
} else {
editableEl.select();
}
};
const copyToClipboard = (value: string): void => {
const el = document.createElement('textarea'); // temporary element
el.value = value;
el.style.position = 'absolute';
el.style.left = '-9999px';
el.readOnly = true; // avoid iOs keyboard opening
el.contentEditable = 'true';
document.body.appendChild(el);
selectText(el, 0, value.length);
document.execCommand('copy');
document.body.removeChild(el);
};
export { copyToClipboard };
Ответ 8
Этот работал для меня для элемента ввода только для чтения.
copyText = input => {
const isIOSDevice = navigator.userAgent.match(/ipad|iphone/i);
if (isIOSDevice) {
input.setSelectionRange(0, input.value.length);
} else {
input.select();
}
document.execCommand('copy');
};
Ответ 9
Моя функция копирования ios и других браузеров в буфер обмена после тестирования на ios: 5c, 6,7
/**
* Copies to Clipboard value
* @param {String} valueForClipboard value to be copied
* @param {Boolean} isIOS is current browser is Ios (Mobile Safari)
* @return {boolean} shows if copy has been successful
*/
const copyToClipboard = (valueForClipboard, isIOS) => {
const textArea = document.createElement('textarea');
textArea.value = valueForClipboard;
textArea.style.position = 'absolute';
textArea.style.left = '-9999px'; // to make it invisible and out of the reach
textArea.setAttribute('readonly', ''); // without it, the native keyboard will pop up (so we show it is only for reading)
document.body.appendChild(textArea);
if (isIOS) {
const range = document.createRange();
range.selectNodeContents(textArea);
const selection = window.getSelection();
selection.removeAllRanges(); // remove previously selected ranges
selection.addRange(range);
textArea.setSelectionRange(0, valueForClipboard.length); // this line makes the selection in iOS
} else {
textArea.select(); // this line is for all other browsers except ios
}
try {
return document.execCommand('copy'); // if copy is successful, function returns true
} catch (e) {
return false; // return false to show that copy unsuccessful
} finally {
document.body.removeChild(textArea); // delete textarea from DOM
}
};
ответ выше о contenteditable = true. Я думаю, что принадлежит только div. А для <textarea>
это не применимо.
Переменная isIOS может быть проверена как
const isIOS = navigator.userAgent.match(/ipad|ipod|iphone/i);
Ответ 10
Это то, что сработало для меня. Код протестирован на всех последних браузерах и работает.
function copyToClipboard(textToCopy) {
var textArea;
function isOS() {
//can use a better detection logic here
return navigator.userAgent.match(/ipad|iphone/i);
}
function createTextArea(text) {
textArea = document.createElement('textArea');
textArea.readOnly = true;
textArea.contentEditable = true;
textArea.value = text;
document.body.appendChild(textArea);
}
function selectText() {
var range, selection;
if (isOS()) {
range = document.createRange();
range.selectNodeContents(textArea);
selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
textArea.setSelectionRange(0, 999999);
} else {
textArea.select();
}
}
function copyTo() {
document.execCommand('copy');
document.body.removeChild(textArea);
}
createTextArea(textToCopy);
selectText();
copyTo();
}
Идея состоит в том, чтобы создать поддельную текстовую область, добавить ее в DOM, установить contentEditable & читать только как правда. Создайте диапазон для выбора нужного элемента и добавьте его в окно выбора. Установите диапазон выбора для всего элемента. А затем запустите execCommand ('copy'). Вы можете заметить огромное количество (999999) внутри метода setSelectionRange(). Ну, это чтобы покрыть все, что может быть внутри элемента.
Узнайте больше о диапазоне от MDN Docs: https://developer.mozilla.org/en-US/docs/Web/API/Range
Тестовый запуск (работает в следующей комбинации устройства и браузера)
iPhone (iOS> = 10) - Safari, Chrome
Android - Chrome, FF
Mac - Chrome, FF, Safari
Windows - Chrome, IE, FF
Я не упомянул версии специально, потому что я тестировал последние версии, доступные мне на момент написания этого поста. Вот подробное изложение того же: https://josephkhan.me/javascript-copy-clipboard-safari/
Ответ 11
<input id="copyIos" type="hidden" value="">
var clipboard = new Clipboard('.copyUrl');
//兼容ios复制
$('.copyUrl').on('click',function() {
var $input = $('#copyIos');
$input.val(share_url);
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
clipboard.on('success', function(e) {
e.clearSelection();
$.sDialog({
skin: "red",
content: 'copy success!',
okBtn: false,
cancelBtn: false,
lock: true
});
console.log('copy success!');
});
} else {
$input.select();
}
//document.execCommand('copy');
$input.blur();
});