Подтвердить что ты не робот

Скопировать в буфер обмена с помощью 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, где моя функция успешно возвращается, но данные не копируются в буфер обмена вообще. Что вызывает это и как я могу решить эту проблему?

4b9b3361

Ответ 1

Обновить! iOS> = 10

Похоже, с помощью диапазонов выделения и небольшого взлома можно напрямую скопировать в буфер обмена на iOS (> = 10) Safari. Я лично проверял это на iPhone 5C iOS 10.3.3 и iPhone 8 iOS 11.1. Тем не менее, существуют некоторые ограничения:

  1. Текст может быть скопирован только из элементов <input> и <textarea>.
  2. Если элемент, содержащий текст, не находится внутри <form>, то он должен быть contenteditable.
  3. Элемент, содержащий текст, не должен быть readonly (хотя вы можете попробовать, это не "официальный" метод, описанный где-либо).
  4. Текст внутри элемента должен находиться в диапазоне выделения.

Чтобы покрыть все эти четыре "требования", вам необходимо:

  1. Поместите текст для копирования в элемент <input> или <textarea>.
  2. Сохраните старые значения contenteditable и readonly элемента, чтобы иметь возможность восстановить их после копирования.
  3. Измените contenteditable на true и readonly на false.
  4. Создайте диапазон, чтобы выбрать нужный элемент и добавить его в окно выбора.
  5. Установите диапазон выбора для всего элемента.
  6. Восстановите предыдущие значения contenteditable и readonly.
  7. Запустите 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();
                });