Класс копирования в буфер обмена JavaScript

Я сделал небольшую копию скрипта в буфер обмена.

Я сделал метод под названием copyToClipboard что делает следующее:

  1. создать поле ввода;
  2. читать содержимое HTML-элемента;
  3. установите значение поля ввода для ранее прочитанного содержимого;
  4. добавить поле ввода в контейнер;
  5. выделить и скопировать его значение в буфер обмена;
  6. удалить поле ввода (больше не полезно);
class ActivationCodeComponent{
  constructor() {
    this.copyButton = document.querySelector('#copy_button');
  }

  copyToClipboard () {
    const el = document.createElement('input');
    const codeBox = document.querySelector('#box');
    const activationCodeContainer = document.querySelector('#activation_code');
    el.value = activationCodeContainer.textContent;
    el.setAttribute('readonly', '');
    codeBox.appendChild(el);
    el.select();
    document.execCommand('copy');
    codeBox.removeChild(el);
  }
  
  handleClick() {
    this.copyButton.addEventListener("click", this.copyToClipboard);
  }

  init() {
    this.handleClick();
  }
}
const activationCodeComponent = new ActivationCodeComponent();
activationCodeComponent.init();
.container  {
   text-align: center;
}

#box  {
  border: 1px solid #ccc;
  width: 200px;
  margin: 20px auto;
  padding: 0 10px;
}

.code {
  letter-spacing: 2px;
  color: #069;
  font-size: 16px;
  font-weight: bold;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container">
  <div id="box">
    <div class="label text-uppercase small">Your activation code</div>
    <div class="code" id="activation_code">B12A7</div>
  </div>
  <button id="copy_button" class="btn btn-sm btn-success text-uppercase">Copy code</button>
 </div>

Проблема

Действительность сценария слишком зависит от набора элементов HTML с используемыми мной идентификаторами.

Можно ли сделать его более гибким?

1 ответ
1

Стиль

Не могу придраться к стилю и планировке.

Необходимость позвонить init что является косвенным призывом к handleClick швы избыточны. Сделайте это в конструкторе.

Я озадачен, почему вы установили readonly к '' (el.setAttribute('readonly', '');) При чем тут копирование в буфер обмена.

Имена

Именование очень плохое, а также вы используете несогласованные соглашения об именах в JS (camelCase) и HTML (все идентификаторы — snake_case)

Имена должны предоставлять информацию, необходимую для понимания его поведения и содержания.

  • ActivationCodeComponent немного неясен, не указывая, что он делает.

  • .handleClick Не обрабатывает щелчок (.copyToClipboard делает это) .handleClick добавляет прослушиватель кликов

  • el Слишком коротко и непонятно. Может быть inputEl

  • codeBox Какие??? Может быть inputContainer

  • activationCodeContainer ничего не содержит. Может быть copyTextSrcEl или же textSourceElement

Базовая перезапись

Перепишите, используя document.execCommand

Обратите внимание, что перезапись не возвращает пригодный для использования объект, поэтому нет необходимости его хранить.

class ActivationCodeComponent{
    constructor() {
        function copyToClipboard () {
            inputEl.value = textSrcEl.textContent;
            inputContainer.appendChild(inputEl);
            inputEl.select();
            document.execCommand('copy');
            inputContainer.removeChild(inputEl);
        }  
        const inputEl = document.createElement('input');
        const inputContainer = document.querySelector('#box');
        const textSrcEl = document.querySelector('#activation_code');
        document.querySelector('#copy_button').addEventListener("click", copyToClipboard);
    }
}
new ActivationCodeComponent();

или же

activationCodeComponent();
function activationCodeComponent() {
    function copyToClipboard () {
        inputEl.value = textSrcEl.textContent;
        inputContainer.appendChild(inputEl);
        inputEl.select();
        document.execCommand('copy');
        inputContainer.removeChild(inputEl);
    }  
    const inputEl = document.createElement('input');
    const inputContainer = document.querySelector('#box');
    const textSrcEl = document.querySelector('#activation_code');
    document.querySelector('#copy_button').addEventListener("click", copyToClipboard);
}

Проблемы

Устарело

Document.execCommand является устаревшей функцией и НЕ ДОЛЖЕН использоваться.

Исправить

Вы должны использовать Clipboard.writeText для добавления содержимого в буфер обмена. Обратите внимание, что перезапись Code Review не будет иметь разрешения на буфер обмена и, следовательно, завершится ошибкой.

Возможность создания и состояние

Лучший способ избежать ошибок — сделать невозможным неправильное использование кода.

Никогда не допускайте противоречивого или пересекающегося поведения. В том виде, в каком есть код, есть десятки способов его использования, которые могут привести к непредсказуемому поведению.

  • Нет ничего, что могло бы помешать объекту ActivationCodeComponent создается более одного раза.

  • Состояние созданного объекта раскрывается, что делает все его поведение ненадежным.

Исправить

Нет необходимости создавать инстанцируемый объект.

  • Используйте область действия функции, чтобы ограничить поведение и защитить состояние.
  • Убедитесь, что функцию можно вызвать только один раз.

Отзывы пользователей

Нет обратной связи с пользователем, чтобы указать, сделал ли что-либо нажатие кнопки. Всегда давайте обратную связь обо всех действиях, какими бы тривиальными они ни были.

Исправить

При перезаписи добавляется простой элемент сообщения, отображающий результат действия копирования. Сообщение исчезнет через 3 секунды.

Вопрос

Ты пишешь

«Действительность сценария слишком зависит от набора HTML-элементов с идентификаторами, которые я использовал».

«Можно ли сделать его более гибким?»

Есть много способов сделать код более гибким.

  • Передайте код необходимые элементы
  • Используйте Element’s Element.dataset отмечать объекты

Пример отмечает кнопку копирования с запросом, необходимым для поиска источника текста.

 const copyBtn = document.querySelector("[data-copy-src]");
 const copySrcEl = document.querySelector(copyBtn.dataset.copyButton);
<div class="code" id="activationCodeEl">B12A7</div>
<button id="copyButton" data-copy-src="https://codereview.stackexchange.com/questions/259581/#copySrcEl">Copy code</button>

Переписать

Пример перемещает запросы элементов из функции. Для использования вы должны передать все элементы, необходимые для работы функции.

Перезапись выполняет следующие

  • Отмечает функцию, чтобы код запускался только один раз.
  • Проверяет все элементы, чтобы убедиться, что они instanceof Element.
const isElement = obj => obj instanceof Element;
const query = (qStr, root = document) => root.querySelector(qStr);
const queries = (qObj, root) => (
    Object.entries(qObj).forEach(([key, qStr]) => qObj[key] = query(qStr, root)),
    qObj
)
const copyElements = queries({
    button: "#copyButton", 
    textSrcEl: "#activationCodeEl", 
    messageEl: "#copyMessageEl",
});

copyToClipboard(copyElements);

function copyToClipboard({button, textSrcEl, messageEl}) {
    const okMessage = "Text copied to system copy buffer";
    const failMessage = "Text failed to copy";
    const okTypeClass = "copyOK", failTypeClass = "copyFail";
    var messageClearHdl;
    
    if (!isElement(button) || !isElement(textSrcEl) || !isElement(messageEl)) { return }
    if (copyToClipboard.active) { return }
    copyToClipboard = Object.freeze(
        Object.assign(copyToClipboard, { active: Symbol.for("activeCopy") })
    );

    function showMessage(text, typeClass) {
        clearTimeout(messageClearHdl);
        messageEl.textContent = text;
        messageEl.className = typeClass;
        messageClearHdl = setTimeout(() => {
                messageEl.textContent = "";
                messageEl.className = "hide";
            }, 3000);
    }
    button.addEventListener("click", () =>
        navigator.clipboard.writeText(textSrcEl.textContent).then(
            () => showMessage(okMessage, okTypeClass),
            () => showMessage(failMessage, failTypeClass)
        ));
}
.container  {
   text-align: center;
}

#box  {
  border: 1px solid #ccc;
  width: 200px;
  margin: 20px auto;
  padding: 0 10px;
}

.code {
  letter-spacing: 2px;
  color: #069;
  font-size: 16px;
  font-weight: bold;
}
.hide {
    display: none;
}
.copyOK {
    margin-top: -18px;
    font-family: arial;
    display: block;
}
.copyFail {
    margin-top: -18px;
    color: red;
    font-family: arial;
    display: block;
}
<div class="container">
  <div id="box">
    <div class="label text-uppercase small">Your activation code</div>
    <div class="code" id="activationCodeEl">B12A7</div>
  </div>
  <div id="copyMessageEl" class="hide"></div>
  <button id="copyButton" class="btn btn-sm btn-success text-uppercase">Copy code</button>
 </div>

Конечно, многие подумают, что безопасность контекста превыше всего. Помимо гарантии того, что код будет вести себя должным образом, нет необходимости включать безопасность контекста.

const query = (qStr, root = document) => root.querySelector(qStr);
const queries = (qObj, root) => (
Object.entries(qObj).forEach(([key, qStr]) => qObj[key] = query(qStr, root)), qObj);

// example only so not making call
//copyToClipboard(queries({
//button: "#copyButton", textSrcEl: "#activationCodeEl", messageEl: "#copyMessageEl",
//}));

function copyToClipboard({button, textSrcEl, messageEl}) {
const okMessage = "Text copied to system copy buffer";
const failMessage = "Text failed to copy";
const okTypeClass = "copyOK", failTypeClass = "copyFail";
var messageClearHdl;

function showMessage(text, typeClass) {
    clearTimeout(messageClearHdl);
    messageEl.textContent = text;
    messageEl.className = typeClass;
    messageClearHdl = setTimeout(() => {
            messageEl.textContent = "";
            messageEl.className = "hide";
        }, 3000
    );
}
button.addEventListener("click", () => {
    navigator.clipboard.writeText(textSrcEl.textContent).then(
        () => showMessage(okMessage, okTypeClass),
        () => showMessage(failMessage, failTypeClass)
    );
});
}

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *