Это потому, что элемент в HTML является встроенным, заменяемым элементом .
Правилами установки размера встроенных заменяемых элементов являются
Если для 'height' и 'width' оба вычислили значения 'auto' и
элемент также имеет собственную ширину, то эта внутренняя ширина является
используемое значение 'width'.
Если для 'height' и 'width' оба вычислили значения 'auto' и
элемент не имеет внутренней ширины, но имеет собственную высоту и
внутреннее соотношение; или если «ширина» имеет вычисленное значение «авто»,
«высота» имеет другое вычисленное значение, а элемент имеет
внутреннее соотношение; тогда используемое значение 'width':
(used height) * (intrinsic ratio)
Если для 'height' и 'width' оба вычислили значения 'auto' и
элемент имеет внутреннее соотношение, но не имеет внутренней высоты или ширины, тогда
используемое значение 'width' не определено в CSS 2.2. Тем не менее, это
Предполагается, что если ширина содержащего блока сама по себе не зависит
на ширине заменяемого элемента, то используемое значение 'ширина'
рассчитывается из уравнения ограничения, используемого для уровня блока,
незаменяемые элементы в нормальном потоке.
В противном случае, если у 'width' есть вычисленное значение 'auto', и элемент
имеет внутреннюю ширину, то эта внутренняя ширина является используемым значением
'Ширина'.
В противном случае, если «width» имеет вычисленное значение «auto», но ни один из
вышеуказанные условия выполняются, тогда используемое значение 'width' становится
300px. Если 300px слишком широки, чтобы соответствовать устройству, UA должны использовать
ширина самого большого прямоугольника, который имеет соотношение 2: 1 и соответствует
устройство вместо.
В первом случае мы попадаем прямо в последний вариант
(width: auto, height: auto) => height = 300, width = height * 1 / 2 = 150
Во втором случае для них установлены неавтоматические значения, поэтому используются эти значения.
В третьем случае установка атрибута viewBox определяет внутреннее соотношение , но не внутренний размер , поэтому мы попадаем в третий случай, который является своего рода неопределенным поведением , даже если предполагается, что « используемое значение« ширины »вычисляется из уравнения ограничения, используемого для незаменяемых элементов уровня блока в нормальном потоке. ».
Это значит width = 100%, height = width * ratio = width * 150 / 200
).
const width = cont.offsetWidth;
const ratio = (150 / 200); // viewBox's height / viewBox's width
const height = width * ratio;
console.log('calculated height', height);
console.log('measured height', elem.getBoundingClientRect().height);
#elem {background: yellow;}
<p id="cont">
<svg id="elem" viewBox="100 100 200 150"></svg>
</p>