Avatar ·

Разница между .prop() и .attr() в jQuery: почему свойства и атрибуты ведут себя по-разному?

Всем привет! Я новичок в JavaScript и jQuery, и никак не могу понять разницу между методами `.prop()` и `.attr()`. Читал документацию, но путаница остаётся. Вот конкретный пример, который меня озадачил. У меня есть чекбокс: ```html ``` Когда я пытаюсь получить его состояние с помощью jQuery, я получаю разные результаты: ```javascript // Первый способ console.log($('#myCheckbox').attr('checked')); // Выводит "checked" // Второй способ console.log($('#myCheckbox').prop('checked')); // Выводит true ``` Почему так? Ведь в HTML атрибут `checked` явно указан. Я ожидал, что оба метода вернут одно и то же. Ещё больше я запутался, когда попробовал динамически менять состояние чекбокса через JavaScript: ```javascript $('#myCheckbox').prop('checked', false); console.log($('#myCheckbox').attr('checked')); // Всё ещё "checked"! ``` То есть, даже после того, как я снял галочку программно, `.attr()` всё равно говорит, что чекбокс отмечен. Это баг? Или я что-то не так понимаю? Объясните, пожалуйста, на пальцах: в каких случаях использовать `.prop()`, а в каких `.attr()`? Есть ли ситуации, когда их можно менять местами без последствий? И почему вообще в jQuery есть два похожих метода для работы с атрибутами? Заранее спасибо за помощь! <<>>

Ключевое различие: атрибуты HTML vs свойства DOM

Вы столкнулись с классической путаницей, которая возникает у многих разработчиков. Дело в том, что HTML-атрибуты (то, что вы пишете в разметке) и DOM-свойства (то, что JavaScript видит в объекте элемента) — это две разные вещи, хотя они часто синхронизированы.

Представьте себе HTML-элемент как человека. HTML-атрибут — это его имя, записанное в паспорте при рождении. DOM-свойство — это то, как его называют друзья в данный момент. Имя в паспорте не меняется, а обращение может быть разным.

Как это работает на практике

Когда браузер парсит HTML, он создаёт DOM-дерево. Для каждого атрибута создаётся соответствующее свойство, но не всегда с одинаковым поведением. Рассмотрим ваш пример с чекбоксом:

<input type="checkbox" id="myCheckbox" checked="checked">

Здесь:

  • HTML-атрибут checked — это строка "checked". Он всегда будет "checked", пока вы не измените исходный HTML.
  • DOM-свойство checked — это булево значение (true/false), которое отражает текущее состояние чекбокса в браузере.

Метод .attr() работает с HTML-атрибутами (как в исходном коде). Метод .prop() — с DOM-свойствами (как в реальном времени).

Почему .attr('checked') не обновляется

Когда вы программно снимаете галочку:

$('#myCheckbox').prop('checked', false);

Вы меняете только DOM-свойство checked на false. HTML-атрибут checked="checked" остаётся нетронутым в DOM-дереве (хотя в исходном HTML он, конечно, не изменится). Поэтому .attr('checked') продолжает возвращать строку "checked".

Правила использования (запомните раз и навсегда)

Используйте .prop() когда:

  • Работаете с булевыми атрибутами: checked, disabled, selected, readonly
  • Нужно получить текущее состояние элемента (например, отмечен ли чекбокс сейчас)
  • Работаете со свойствами, которых нет в HTML, но есть в DOM: nodeName, tagName, selectedIndex

Используйте .attr() когда:

  • Нужно прочитать или изменить пользовательские атрибуты (data-*), которые вы сами задали в HTML
  • Работаете со стандартными атрибутами, которые не являются булевыми: href, src, title, alt
  • Хотите получить значение атрибута именно таким, как он записан в HTML (например, полный URL в href против относительного пути)

Наглядный пример с ссылкой

<a href="https://example.com" id="myLink">Ссылка</a>

<script>
  // .attr() возвращает то, что в HTML
  console.log($('#myLink').attr('href')); // "https://example.com"
  
  // .prop() возвращает полный абсолютный URL (браузер преобразует)
  console.log($('#myLink').prop('href'));  // "https://example.com/" (слеш в конце)
</script>

Здесь разница в том, что .prop() возвращает нормализованное браузером значение, а .attr() — буквально то, что написано в коде.

Золотое правило для новичков

Если сомневаетесь — используйте .prop() для чтения/изменения состояния элементов (чекбоксы, радиокнопки, выпадающие списки) и .attr() для чтения атрибутов, которые не меняются во время работы страницы. В 90% случаев вам нужен именно .prop().

<<>>

Дополнение: когда .attr() и .prop() дают одинаковый результат

На самом деле, для большинства стандартных атрибутов (не булевых) оба метода возвращают одно и то же. Например:

<div id="myDiv" class="container"></div>

console.log($('#myDiv').attr('class')); // "container"
console.log($('#myDiv').prop('className')); // "container"

Обратите внимание: для свойства class в DOM используется имя className, потому что class — зарезервированное слово в JavaScript. jQuery любезно скрывает эту разницу в методе .prop().

Практический совет: как не ошибаться

Если вы пишете плагин или библиотеку, где важна совместимость — всегда проверяйте тип возвращаемого значения. Для булевых свойств используйте .prop():

// Правильно
function isChecked(selector) {
    return $(selector).prop('checked'); // true/false
}

// Неправильно
function isChecked(selector) {
    return $(selector).attr('checked'); // "checked" или undefined
}

Также помните, что начиная с jQuery 1.6, метод .attr() для булевых атрибутов ведёт себя по-другому, чем в старых версиях. Если вы поддерживаете старый код — проверьте версию jQuery.

В современных проектах рекомендую использовать .prop() для всего, что связано с состоянием элементов, и .attr() только для пользовательских data-атрибутов и атрибутов, которые вы точно хотите читать как строки из HTML.