Изменение CRM-формы для опросов: ниже собраны практические примеры доработки полей, интерфейса и автозаполнения значений в формах Битрикс24.

В этой статье по шагам разбираем улучшение интерфейса формы, автоматическое удаление повторных названий в множественных полях и изменение названия формы на ФИО оцениваемого сотрудника, а по ходу собираем выводы, которые можно сразу примерить на свои процессы.

Изменение строкового поля в форме.

Проблема: Стандартные поля input[type=“string”] не подходили для ввода большого текста, нужно добавить перенос текста по строкам, если он не помещается в поле, а также возможность изменить его размер.

Решение: Добавить код, которые ищет все input[type=“string”] на странице, скрывает его и создает новый элемент textarea. Оба этих элемента связываются уникальным dataset, через который настроена передача данных.

function replaceStringInputsWithTextareas() {
    const stringInputs = document.querySelectorAll('input[type="string"]');
 
    stringInputs.forEach((input) => {
      if (input.dataset.textareaReplaced) return;
      const linkId =
     "textarea_" + Date.now() + "_" + Math.random().toString(8).substr(2, 4);
      input.dataset.textareaLinkId = linkId;
      input.dataset.textareaReplaced = "true";
      const textarea = document.createElement("textarea");
      textarea.dataset.linkedInputId = linkId;
      textarea.className = input.className;
      textarea.value = input.value || "";
      textarea.style.resize = "vertical";
      input.style.display = "none";
 const container = document.createElement("div");
      container.className = linkId;
      input.parentNode.insertBefore(container, input);
      container.appendChild(textarea);
      container.appendChild(input);
      textarea.addEventListener("input", function () {
        input.value = this.value;
        input.dispatchEvent(new Event("input", { bubbles: true }));
      });
    });
  }
  document.addEventListener("DOMContentLoaded", function () {
    setTimeout(function () {
      replaceStringInputsWithTextareas();
    }, 200);
  });

Было*(Текст в 1 строку , просто исчезает если не влазит)*

Стало*(Текст переносится, можно расширить поле, если он не влазит)*


Улучшение интерфейса формы

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

Решение: Изменении логики названия полей, теперь они находятся над полями, а также изменение структуры формы, при наличии подсказки для поля она переносится под вопрос, над полем.

function organizeFieldsSafe() {
    const containers = document.querySelectorAll(
      ".b24-form-control-string, .b24-form-control-list, .b24-form-control-text"
    );
    containers.forEach((container) => {
      if (container.dataset.organized) return;
      const label = container.querySelector(".b24-form-control-label");
      const comment = container.querySelector(".b24-form-control-comment");
      if (label) {
        container.prepend(label);
      }
      if (comment && label) {
        label.insertAdjacentElement("afterend", comment);
      }
      container.dataset.organized = "true";
    });
  document.addEventListener("DOMContentLoaded", function () {
    setTimeout(function () {
      organizeFieldsSafe();
   }, 200);
  });
<style>
  .b24-form-control-label {
    margin-bottom: 8px !important;
    display: block !important;
    font-weight: 600 !important;
    color: #333 !important;
    position: relative !important;
    left: 0 !important;
    transform: translateY(0%) !important;
    right: 0 !important;
    white-space: normal !important;
    padding-left: 0 !important;
  }

Было:(Названия полей внутри, подсказки для поля под полем)

Стало:(Названия и подсказки для полей вынесены над полем)


Автоматическое удаление повторных названий в множественных полях

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

**Решение:**Находить множественные поля и проверять количество названий. Все кроме первого удалять.

function removeExtraLabels() {
    const containers = document.querySelectorAll(".b24-form-control-string, .b24-form-control-list, .b24-form-control-text");
    let totalRemoved = 0;
    containers.forEach((container, containerIndex) => {
      const labels = container.querySelectorAll(".b24-form-control-label");
      const fields = container.querySelectorAll("input, textarea, select");
      if (fields.length > 1 && labels.length > 1) {
        for (let i = labels.length - 1; i >= 1; i--) {
          labels[i].remove();
          totalRemoved++;
        }
      }
    });
return totalRemoved;
  }
  document.addEventListener("DOMContentLoaded", function () {
    setTimeout(function () {
      removeExtraLabels();
    }, 200);
  });
  setInterval(removeExtraLabels, 1000);

Было:(Названия множественного поля находятся в поле, поэтому дублируются)

Стало:(Названия сверху, удаляются при добавлении новых вариантов ответа)


Изменение названия формы на ФИО оцениваемого сотрудника

**Проблема:**При оценке необходимо понимать какого сотрудника мы оцениваем, нужно вывести его ФИО в название формы

Решение: При отправке URL формы, необходимо добавить параметр name, в котором передать ФИО оцениваемого сотрудника, после чего записать ФИО в название формы

ffunction getNameFromUrl() {
    const urlParams = new URLSearchParams(window.location.search);
    return urlParams.get("name");
  }
  function updateFormTitle() {
    const name = getNameFromUrl();
    if (name) {
      const formTitle = document.querySelector(".b24-form-header-title");
      formTitle.textContent = name;
    }
  }
  document.addEventListener("DOMContentLoaded", function () {
    setTimeout(function () {
      updateFormTitle();
    }, 200);
  });

Было:(Название формы вынесено перед ней, статичное)

Стало:(Изменяется в зависимости от параметра name, переданного в ссылке)


Общий код для всех настроек:

<script>
  function replaceStringInputsWithTextareas() {
    const stringInputs = document.querySelectorAll(&#039;input[type="string"]&#039;);
 
    stringInputs.forEach((input) => {
      if (input.dataset.textareaReplaced) return;
 
      const linkId =
        "textarea_" + Date.now() + "_" + Math.random().toString(8).substr(2, 4);
      input.dataset.textareaLinkId = linkId;
      input.dataset.textareaReplaced = "true";
 
      const textarea = document.createElement("textarea");
      textarea.dataset.linkedInputId = linkId;
      textarea.className = input.className;
      textarea.value = input.value || "";
      textarea.style.resize = "vertical";
 
      input.style.display = "none";
 
      const container = document.createElement("div");
      container.className = linkId;
            input.parentNode.insertBefore(container, input);
      container.appendChild(textarea);
      container.appendChild(input);
 
      textarea.addEventListener("input", function () {
        input.value = this.value;
        input.dispatchEvent(new Event("input", { bubbles: true }));
      });
    });
  }
  function organizeFieldsSafe() {
    const containers = document.querySelectorAll(
      ".b24-form-control-string, .b24-form-control-list, .b24-form-control-text"
    );
    containers.forEach((container) => {
      if (container.dataset.organized) return;
      const label = container.querySelector(".b24-form-control-label");
      const comment = container.querySelector(".b24-form-control-comment");
      if (label) {
        container.prepend(label);
      }
      if (comment && label) {
        label.insertAdjacentElement("afterend", comment);
      }
      container.dataset.organized = "true";
    });
  }
  function removeExtraLabels() {
    const containers = document.querySelectorAll(
      ".b24-form-control-string, .b24-form-control-list, .b24-form-control-text"
    );
    let totalRemoved = 0;
    containers.forEach((container, containerIndex) => {
      const labels = container.querySelectorAll(".b24-form-control-label");
      const fields = container.querySelectorAll("input, textarea, select");
 
      if (fields.length > 1 && labels.length > 1) {
        // Удаляем все лейблы кроме первого (сохраняем индекс 0)
        for (let i = labels.length - 1; i >= 1; i--) {
          labels[i].remove();
          totalRemoved++;
        }
      }
    });
    return totalRemoved;
  }
  function getNameFromUrl() {
    const urlParams = new URLSearchParams(window.location.search);
        return urlParams.get("name");
  }
  function updateFormTitle() {
    const name = getNameFromUrl();
    if (name) {
      const formTitle = document.querySelector(".b24-form-header-title");
      formTitle.textContent = name;
    }
  }
  document.addEventListener("DOMContentLoaded", function () {
    setTimeout(function () {
      replaceStringInputsWithTextareas();
      organizeFieldsSafe();
      removeExtraLabels();
      updateFormTitle();
    }, 500);
  });
 
  setInterval(removeExtraLabels, 1000);
</script>
 
<style>
  .b24-form-control-label {
    margin-bottom: 8px !important;
        display: block !important;
    font-weight: 600 !important;
    color: #333 !important;
    position: relative !important;
    left: 0 !important;
    transform: translateY(0%) !important;
    right: 0 !important;
    white-space: normal !important;
    padding-left: 0 !important;
  }
  .b24-form-control-comment {
    margin-bottom: 4px !important;
    display: block !important;
    font-size: 12px !important;
    color: #666 !important;
  }
  .b24-form-control-list
    .b24-form-control-icon-after
    .b24-form-control-not-empty.b24-form-control,
  .b24-form-control-list
    .b24-form-control-icon-after
    .b24-form-control-not-empty.b24-form-control-label {
    padding: 0 80px 0 15px;
  }
  textarea.b24-form-control {
    min-height: 80px;
    resize: vertical;
    width: 100%;
    box-sizing: border-box;
    padding: 12px;
    border: 1px solid #c6cdd3;
    border-radius: 2px;
  }
</style>