← Все статьи

SiteGuard12 мин чтения

Не работают кнопки на сайте: как найти ошибку JavaScript в DevTools

Если не работают кнопки на сайте, JavaScript проверяют по конкретному действию: что нажал посетитель, какой результат ожидался и на каком шаге выполнение остановилось. Открывающаяся страница ещё не подтверждает исправность меню, фильтра или формы. При этом красная строка в консоли может относиться к рекламному виджету и не иметь отношения к сломанной кнопке.

Рабочий порядок диагностики: воспроизвести действие, сохранить сообщения Console, найти относящееся к нему исключение, проверить обработчик события и остановить выполнение в нужном месте. Если действие должно обращаться к серверу, дополнительно проследить запрос в Network. Ниже — инструкция для Chrome DevTools и безопасный учебный пример без отправки данных.

1. Запишите один воспроизводимый сценарий

Начните с точного описания. Вместо «форма сломалась» запишите: открыть определённую страницу, выбрать вариант товара, нажать «Показать детали»; ожидается раскрытие блока, фактически ничего не меняется. Если проблема проявляется только после возврата назад, повторного открытия окна или смены фильтра, эти действия входят в сценарий. Прямое открытие страницы может пройти успешно и не повторить исходный сбой.

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

Руководство Chrome по отладке JavaScript начинает поиск с последовательности действий, стабильно воспроизводящей ошибку. Владелец сайта уже на этом этапе может существенно помочь разработчику. Короткая запись экрана с точным маршрутом полезнее большого набора несвязанных скриншотов.

Для действий, создающих заказ, списание или внешнее уведомление, используйте согласованную тестовую среду. Если результат рабочего запроса неизвестен, сначала выясните его состояние. Многократные нажатия ради повторения ошибки могут создать реальные повторные операции, даже когда интерфейс выглядит неподвижным.

2. Откройте Console до повторения действия

В Chrome нажмите Ctrl+Shift+J, а на macOS — Command+Option+J. Откроется Console. Для начала достаточно просмотра сообщений: вводить команды, пароли или токены не требуется. Если кто-то предлагает вставить неизвестный код для «починки кнопки», это не является частью описанной диагностики.

Очистите журнал перед контрольной загрузкой, затем перезагрузите страницу и повторите подготовленные шаги один раз. Сохраните ошибки загрузки: раннее исключение могло помешать регистрации обработчика, поэтому после самого нажатия новых сообщений не появится. Если сценарий меняет страницу, включите Preserve log, чтобы не потерять предыдущие записи.

В справочнике Console описаны фильтры уровней, URL и контекстов. Сначала убедитесь, что уровень Error видим и старый текстовый фильтр ничего не скрывает. Затем можно сузить сообщения фильтром url: по выбранному адресу скрипта. Для встроенного фрейма проверьте выбранный контекст и настройку Selected context only.

Фильтр помогает читать журнал, но не доказывает, что остальные сообщения не относятся к проблеме. Код сайта может загружаться с отдельного домена, а действие внутри фрейма — выполняться в другом контексте. Сохраните исходную картину и только после этого убирайте подтверждённо посторонний шум.

3. Найдите первое исключение, связанное с действием

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

Разверните сообщение и сохраните текст исключения, файл, строку и стек вызовов. Стек показывает путь выполнения к месту сбоя. Переход по ссылке файла открывает соответствующий участок в Sources. Если верхняя запись относится к библиотеке, проследите, откуда её вызвал код приложения; виновность библиотеки из одного имени файла не следует.

Например, сообщение о чтении свойства у null означает, что код ожидал объект, но получил другое значение. Это ещё не объясняет причину: элемент мог отсутствовать, появиться позже или иметь другой идентификатор. Ошибка указывает место обнаружения несоответствия, а проверка переменных помогает найти его источник.

Один сбой способен вызвать цепочку вторичных ошибок. Разбирайте первую подтверждённую причину, затем повторяйте сценарий и смотрите, что осталось. Отсутствие красных сообщений также не подтверждает исправность: обработчик может завершиться по условию, попасть в пустой catch или вовсе не быть вызван.

4. Проверьте, доступна ли кнопка для нажатия

Выберите проблемный элемент через инструмент выбора в Elements. Убедитесь, что перед вами нужная кнопка, а не прозрачный блок поверх неё. Закрывшееся визуально окно иногда оставляет перекрывающий слой; нажатие получает этот слой. Сравните выбранный узел с ожидаемым и проверьте его размеры и расположение. Случайное увеличение z-index не заменяет выяснение причины перекрытия.

Посмотрите состояние кнопки и условия его изменения. Как объясняет справочник MDN по button, атрибут disabled запрещает пользовательское взаимодействие и фокус. Если блокировка должна сниматься после выбора тарифа, проверьте этот переход. Ручное удаление атрибута в инспекторе может подтвердить гипотезу, но не исправляет логику разблокировки.

Далее найдите обработчики в Elements → Event Listeners. Учитывайте делегирование: обработчик может находиться на родительском элементе, а не непосредственно на кнопке. Отсутствие записи только у выбранного узла ещё не означает, что приложение не обрабатывает событие. После динамической перерисовки также проверьте, не заменился ли сам DOM-элемент.

Разделите два вопроса: достигает ли нажатие ожидаемого элемента и выполняется ли нужная функция приложения. Ответ «курсор меняется» не решает ни один из них. Следующий шаг позволяет увидеть выполнение непосредственно.

5. Остановитесь в обработчике или на исключении

Если место обработчика неизвестно, в Sources раскройте Event Listener Breakpoints и выберите событие click в группе Mouse. Повторите нажатие. Такой breakpoint останавливает код слушателя события; он может привести сначала в общий обработчик библиотеки или родительского элемента. По стеку и аргументам найдите ветку, относящуюся к вашей кнопке.

Для исключений в разделе Breakpoints доступны Pause on uncaught exceptions и Pause on caught exceptions. Первый вариант помогает остановиться на исключениях, которые остаются необработанными; второй — также исследовать исключения, которые код перехватывает. В браузерной отладке эти настройки выбираются независимо. Точное поведение описано в документации о точках остановки.

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

Если паузы нет, проверьте, что точки остановки включены, выбран нужный фрейм и исследуется правильное событие. Приложение может начинать действие на другом событии, а обработчик — ещё не быть зарегистрирован. Отсутствие остановки в слушателе не доказывает, что физическое нажатие вообще не происходило. Вернитесь к элементу, его состоянию и загрузке кода.

Когда функция найдена, поставьте точку остановки на нужной строке. В Scope посмотрите локальные переменные, в Call Stack — путь вызова. Переходите по строкам и отмечайте, где фактическое значение расходится с ожидаемым. Пока выполнение на паузе, неподвижный интерфейс ожидаем; снимите паузу перед оценкой результата.

6. Учебный пример: кнопка есть, а нужный элемент не найден

Рассмотрим вымышленную локальную страницу. Кнопка должна показывать уже существующий текст; серверный запрос не требуется. Разметка содержит два элемента:

<button id="open-details" type="button">Показать детали</button>
<p id="details" hidden>Здесь находятся детали товара.</p>

Следующий фрагмент находится в файле проекта и выполняется после построения этой разметки. В нём специально оставлена ошибка селектора. Это учебный код для разбора, а не команда для вставки в консоль рабочего сайта.

const button = document.querySelector("#open-details");
const panel = document.querySelector("#detail");

button.addEventListener("click", () => {
  panel.hidden = false;
});

Кнопка найдена, обработчик зарегистрирован, поэтому страница загружается без исключения на этих строках. При нажатии выполнение доходит до panel.hidden = false и возникает TypeError: переменная panel содержит null. Идентификатор блока — details, а код ищет detail.

Поставьте breakpoint на строке изменения hidden. При остановке проверьте panel в Scope, затем сопоставьте селектор с разметкой. Исправление в этом примере — заменить "#detail" на "#details". После повторной загрузки кнопка раскрывает текст, а исключение исчезает. Отсутствие запроса в Network здесь нормально и не требует ремонта сервера.

Добавление условия «если элемент найден» само по себе может только скрыть ошибку: исключения больше нет, но обязательный блок всё ещё не раскрывается. Защита нужна, когда отсутствие элемента допустимо по устройству страницы. Когда элемент обязателен, исправляют несогласованность разметки и кода, а успех проверяют по видимому результату.

7. Для формы различайте click, submit и проверку полей

Нажатие кнопки и отправка формы — разные этапы. У обычной кнопки, связанной с формой, проверьте type: button не отправляет форму автоматически, а submit предназначен для отправки. Если тип не указан, обычная кнопка внутри формы может начать отправку неожиданно. Эти различия также описаны в справочнике MDN по кнопке.

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

Если приложение управляет отправкой само, найдите обработчик submit, его условия выхода и использование preventDefault(). Отмена стандартного действия не отправляет запрос за разработчика. Ветка должна явно продолжить предусмотренную обработку или показать понятную причину отказа. Одного обработчика клика недостаточно для проверки отправки формы клавиатурой.

Не меняйте тип кнопки и не отключайте проверку полей только ради исчезновения симптома. Сначала определите ожидаемый маршрут: обычная отправка документа или запрос из JavaScript. Затем проверьте именно этот маршрут с корректными и некорректными тестовыми данными.

8. Посмотрите, появился ли ожидаемый запрос

Если действие должно обращаться к серверу, откройте Network до повторения сценария. Убедитесь, что запись включена. При переходе между страницами используйте сохранение журнала. Начните с режима All: фильтр Fetch/XHR удобен для программных запросов, но может скрыть обычную отправку формы с загрузкой документа.

Сопоставьте время нажатия с новой записью. В руководстве по Network описаны статус, инициатор, заголовки и содержимое ответа. Проверьте адрес, метод, ожидаемые данные запроса, затем ответ. Initiator помогает вернуться к коду, который вызвал обращение.

НаблюдениеСледующий шаг
Ожидаемой записи нетПроверить фильтры, обработчик, валидацию и условия до отправки
Запрос есть, возвращён отказРазобрать ответ и соответствующую ветку обработки
Ответ получен, интерфейс не изменилсяПроверить разбор данных и обновление нужного элемента

Статус 200 не доказывает выполнение пользовательской задачи. Ответ может содержать прикладную ошибку, неожиданный формат или страницу входа. Если результат на сервере уже создан, а интерфейс не показал подтверждение, исправляют обработку ответа. Повторять создание той же операции без выяснения исхода не следует.

9. Отделите ошибку обработчика от загрузки и политик браузера

Иногда до обработчика выполнение не доходит, потому что нужный файл не загрузился или браузер не разрешил его использовать. Смотрите на конкретное сообщение и соответствующий ресурс. Ошибки 404, CORS, CSP и MIME нельзя объединять в одну причину «JavaScript отключён».

  • При 404 для файла скрипта сравните запрошенный URL с опубликованными файлами сборки.
  • При сообщении CORS проверьте, разрешён ли JavaScript доступ к ответу другого источника; сам запрос при этом мог уже попасть на сервер.
  • При CSP выясните, какое действие или ресурс блокирует действующая политика.
  • При ошибке MIME сопоставьте ожидаемый тип ресурса, заголовок Content-Type и фактическое содержимое ответа.

Например, вместо JavaScript по нужному адресу может прийти HTML. Это полезная находка для проверки публикации и маршрутизации, а не повод бесконечно менять обработчик клика. Зафиксируйте сообщение, адрес и ответ и передайте проблему владельцу соответствующего участка. Не отключайте защитные политики как универсальное исправление кнопки.

10. Сопоставьте исходник с реальной сборкой

В рабочем сайте ошибка нередко ведёт в минифицированный файл с короткими именами переменных. Форматирование делает его читаемее, но не восстанавливает исходные имена и структуру проекта. Source maps позволяют DevTools сопоставлять опубликованный код с исходным; этот режим описан в справочнике отладки JavaScript.

Проверьте, что карта соответствует именно загруженной сборке. Запишите URL файла, его идентификатор или хеш и версию релиза. Если у разработчика открыт новый исходник, а браузер посетителя получил предыдущий пакет, одинаковые номера строк не обеспечивают правильного сравнения. Сохраните данные до очистки кешей.

Правка в DevTools помогает проверить гипотезу в текущем окружении. Локальная подмена, в том числе через Local Overrides, не становится автоматически исправлением для всех посетителей. Изменение нужно внести в проект, собрать, проверить и опубликовать обычным процессом. После этого повторите сценарий без локальных подмен.

Если source maps недоступны, разработчик всё ещё может исследовать опубликованный файл и сопоставить его с артефактами нужного релиза. Отсутствие карты усложняет чтение, но само по себе не объясняет, почему перестала работать кнопка.

11. Проверьте результат после исправления

Повторите исходный маршрут на исправленной сборке. Недостаточно увидеть пустую Console: должен появиться ожидаемый результат. Для раскрытия блока это видимый контент, для фильтра — правильная выборка, для сохранения — подтверждённое состояние. Проверьте также соседний вариант, который использует тот же обработчик.

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

Отдельно проверьте сценарий без отладочных пауз. Остановка выполнения меняет время происходящего и может скрыть ошибку, связанную с порядком загрузки или ответов. Если кнопка работает только при пошаговом выполнении, исправление ещё не подтверждено. Сравните исходный маршрут без пауз и сохраните условия, при которых результат отличается.

Снимите отладочные паузы и отключите локальные подмены перед итоговой проверкой. После публикации проверьте, что браузер получает исправленный пакет. Если ошибка воспроизводилась только при старом состоянии страницы или после динамического обновления, обычного открытия главной страницы для приёмки недостаточно.

12. Передайте разработчику доказательства и границы проверки

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

Перед отправкой материалов уберите пароли, токены, cookies и персональные данные. Для первичного разбора обычно достаточно короткого обезличенного фрагмента. Если исключения нет, так и напишите: «обработчик не найден», «кнопка disabled» или «событие есть, выполнение выходит по условию» — только то, что действительно проверено.

Формулируйте завершение задачи через результат и проверенный охват: нужная кнопка работает в исходном сценарии, повторной загрузке и согласованных вариантах управления. Работающая главная страница и успешный HTTP-ответ не подтверждают исправность интерактивного элемента.

Для внешнего наблюдения за важными страницами изучите актуальные возможности SiteGuard. Сопоставляйте сведения о доступности с отдельной проверкой пользовательских действий: поиск исключения и подтверждение работы кнопки остаются задачами диагностики приложения.