up
|
@ -1,41 +0,0 @@
|
|||
Есть два варианта.
|
||||
|
||||
1. Можно использовать свойство `elem.style.cssText` и присвоить стиль в текстовом виде. При этом все присвоенные ранее свойства `elem.style` будут удалены.
|
||||
2. Можно назначить подсвойства `elem.style` одно за другим. Этот способ более безопасен, т.к. меняет только явно присваемые свойства.
|
||||
|
||||
Мы выберем второй путь.
|
||||
|
||||
**Описание CSS-свойств:**
|
||||
|
||||
```css
|
||||
.button {
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
border: 2px groove green;
|
||||
display: block;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
width: 100px;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
```
|
||||
|
||||
`*-border-radius`
|
||||
: Добавляет скругленные углы. Свойство присваивается в вариантах для Firefox `-moz-...`, Chrome/Safari `-webkit-...` и стандартное CSS3-свойство для тех, кто его поддерживает (Opera).
|
||||
|
||||
`display`
|
||||
: По умолчанию, у `A` это свойство имеет значение `display: inline`.
|
||||
|
||||
`height`, `line-height`
|
||||
: Устанавливает высоту и делает текст вертикально центрированным путем установки `line-height` в значение, равное высоте. Такой способ центрирования текста работает, если он состоит из одной строки.
|
||||
|
||||
`text-align`
|
||||
: Центрирует текст горизонтально.
|
||||
|
||||
`color`, `font-weight`
|
||||
: Делает текст красным и жирным.
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div>
|
||||
Кнопка:
|
||||
<!-- создайте элемент и расположите его тут -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var a = document.createElement('a');
|
||||
a.className = 'button';
|
||||
a.appendChild(document.createTextNode('Нажми меня'));
|
||||
a.href = '/';
|
||||
|
||||
var s = a.style
|
||||
s.MozBorderRadius = s.WebkitBorderRadius = s.borderRadius = '8px';
|
||||
s.border = '2px groove green';
|
||||
s.display = 'block';
|
||||
s.height = '30px';
|
||||
s.lineHeight = '30px';
|
||||
s.width = '100px';
|
||||
s.textDecoration = 'none';
|
||||
s.textAlign = 'center';
|
||||
s.color = 'red';
|
||||
s.fontWeight = 'bold';
|
||||
|
||||
var div = document.body.children[0];
|
||||
div.appendChild(a);
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,22 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div>
|
||||
Кнопка:
|
||||
<!-- создайте элемент и расположите его тут -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// .. ваш код
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,33 +0,0 @@
|
|||
importance: 3
|
||||
|
||||
---
|
||||
|
||||
# Скругленая кнопка со стилями из JavaScript
|
||||
|
||||
Создайте кнопку в виде элемента `<a>` с заданным стилем, используя JavaScript.
|
||||
|
||||
В примере ниже такая кнопка создана при помощи HTML/CSS. В вашем решении кнопка должна создаваться, настраиваться и добавляться в документ при помощи *только JavaScript*, без тегов `<style>` и `<a>`.
|
||||
|
||||
```html autorun height="50"
|
||||
<style>
|
||||
.button {
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
border: 2px groove green;
|
||||
display: block;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
width: 100px;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<a class="button" href="/">Нажми меня</a>
|
||||
```
|
||||
|
||||
**Проверьте себя: вспомните, что означает каждое свойство. В чём состоит эффект его появления здесь?**
|
||||
|
|
@ -3,12 +3,12 @@
|
|||
z-index: 1000;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
font: normal 20px Georgia;
|
||||
font-size: 20px;
|
||||
background: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
background: red;
|
||||
background: #b80000;
|
||||
color: yellow;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h2>Уведомление</h2>
|
||||
<h2>Notification is on the right</h2>
|
||||
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum
|
||||
|
@ -17,43 +15,30 @@
|
|||
</p>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Показывает уведомление, пропадающее через 1.5 сек
|
||||
*
|
||||
* @param options.top {number} вертикальный отступ, в px
|
||||
* @param options.right {number} правый отступ, в px
|
||||
* @param options.cssText {string} строка стиля
|
||||
* @param options.className {string} CSS-класс
|
||||
* @param options.html {string} HTML-текст для показа
|
||||
*/
|
||||
function showNotification(options) {
|
||||
function showNotification({top = 0, right = 0, className, html}) {
|
||||
|
||||
var notification = document.createElement('div');
|
||||
let notification = document.createElement('div');
|
||||
notification.className = "notification";
|
||||
if (options.cssText) {
|
||||
notification.style.cssText = options.cssText;
|
||||
}
|
||||
notification.style.top = (options.top || 0) + 'px'; // can use cssText
|
||||
notification.style.right = (options.right || 0) + 'px'; // can use cssText
|
||||
if (options.className) {
|
||||
notification.classList.add(options.className);
|
||||
if (className) {
|
||||
notification.classList.add(className);
|
||||
}
|
||||
|
||||
notification.style.top = top + 'px';
|
||||
notification.style.right = right + 'px';
|
||||
|
||||
notification.innerHTML = options.html;
|
||||
document.body.appendChild(notification); // over cover
|
||||
document.body.append(notification);
|
||||
|
||||
setTimeout(function() {
|
||||
document.body.removeChild(notification);
|
||||
}, 1500);
|
||||
setTimeout(() => notification.remove(), 1500);
|
||||
}
|
||||
|
||||
// тест работоспособности
|
||||
var i = 0;
|
||||
setInterval(function() {
|
||||
// test it
|
||||
let i = 1;
|
||||
setInterval(() => {
|
||||
showNotification({
|
||||
top: 10,
|
||||
right: 10,
|
||||
html: 'Привет ' + ++i,
|
||||
html: 'Hello ' + i++,
|
||||
className: "welcome"
|
||||
});
|
||||
}, 2000);
|
||||
|
@ -61,5 +46,4 @@
|
|||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
z-index: 1000;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
font: normal 20px Georgia;
|
||||
font-size: 20px;
|
||||
background: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
background: red;
|
||||
background: #b80000;
|
||||
color: yellow;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h2>Уведомление</h2>
|
||||
<h2>Notification is on the right</h2>
|
||||
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum
|
||||
|
@ -16,29 +14,18 @@
|
|||
temporibus. Tempore dicta ullam delectus numquam consectetur quisquam explicabo culpa excepturi placeat quo sequi molestias reprehenderit hic at nemo cumque voluptates quidem repellendus maiores unde earum molestiae ad.
|
||||
</p>
|
||||
|
||||
<p>В CSS есть готовый класс notification, который можно ставить уведомлению.</p>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Показывает уведомление, пропадающее через 1.5 сек
|
||||
*
|
||||
* @param options.top {number} вертикальный отступ, в px
|
||||
* @param options.right {number} правый отступ, в px
|
||||
* @param options.cssText {string} строка стиля
|
||||
* @param options.className {string} CSS-класс
|
||||
* @param options.html {string} HTML-текст для показа
|
||||
*/
|
||||
function showNotification(options) {
|
||||
// ваш код
|
||||
function showNotification({top = 0, right = 0, className, html}) {
|
||||
/* your code */
|
||||
}
|
||||
|
||||
// тест работоспособности
|
||||
var i = 0;
|
||||
setInterval(function() {
|
||||
// test it
|
||||
let i = 1;
|
||||
setInterval(() => {
|
||||
showNotification({
|
||||
top: 10,
|
||||
right: 10,
|
||||
html: 'Привет ' + ++i,
|
||||
html: 'Hello ' + i++,
|
||||
className: "welcome"
|
||||
});
|
||||
}, 2000);
|
||||
|
@ -46,5 +33,4 @@
|
|||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -2,40 +2,23 @@ importance: 5
|
|||
|
||||
---
|
||||
|
||||
# Создать уведомление
|
||||
# Create a notification
|
||||
|
||||
Напишите функцию `showNotification(options)`, которая показывает уведомление, пропадающее через 1.5 сек.
|
||||
Write a function `showNotification(options)` that a notification: `<div class="notification">` with the given content. The notification should automatically disappear after 1.5 seconds.
|
||||
|
||||
Описание функции:
|
||||
The options are:
|
||||
|
||||
```js
|
||||
/**
|
||||
* Показывает уведомление, пропадающее через 1.5 сек
|
||||
*
|
||||
* @param options.top {number} вертикальный отступ, в px
|
||||
* @param options.right {number} правый отступ, в px
|
||||
* @param options.cssText {string} строка стиля
|
||||
* @param options.className {string} CSS-класс
|
||||
* @param options.html {string} HTML-текст для показа
|
||||
*/
|
||||
function showNotification(options) {
|
||||
// ваш код
|
||||
}
|
||||
```
|
||||
|
||||
Пример использования:
|
||||
|
||||
```js
|
||||
// покажет элемент с текстом "Привет" и классом welcome справа-сверху окна
|
||||
// shows an element with the text "Hello" near the right-top of the window
|
||||
showNotification({
|
||||
top: 10,
|
||||
right: 10,
|
||||
html: "Привет",
|
||||
className: "welcome"
|
||||
top: 10, // 10px from the top of the window (by default 0px)
|
||||
right: 10, // 10px from the right edge of the window (by default 0px)
|
||||
html: "Hello!", // the HTML of notification
|
||||
className: "welcome" // an additional class for the div (optional)
|
||||
});
|
||||
```
|
||||
|
||||
[demo src="solution"]
|
||||
|
||||
Элемент уведомления должен иметь CSS-класс `notification`, к которому добавляется класс из `options.className`, если есть. Исходный документ содержит готовые стили.
|
||||
|
||||
Use CSS positioning to show the element at given top/right coordinates. The source document has the necessary styles.
|
||||
|
|
|
@ -290,6 +290,6 @@ To change the styles:
|
|||
|
||||
- The `style.cssText` property corresponds to the whole `"style"` attribute, the full string of styles.
|
||||
|
||||
To read the resolved styles (after all CSS is applied and final values are calculated):
|
||||
To read the resolved styles (with respect to all classes, after all CSS is applied and final values are calculated):
|
||||
|
||||
- The `getComputedStyles(elem[, pseudo])` returns the style-like object with them. Read-only.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Решение: `elem.scrollHeight - elem.scrollTop - elem.clientHeight`.
|
|
@ -0,0 +1,11 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Найти размер прокрутки снизу
|
||||
|
||||
Свойство `elem.scrollTop` содержит размер прокрученной области при отсчете сверху. А как подсчитать размер прокрутки снизу?
|
||||
|
||||
Напишите соответствующее выражение для произвольного элемента `elem`.
|
||||
|
||||
Проверьте: если прокрутки нет вообще или элемент полностью прокручен -- оно должно давать ноль.
|
|
@ -0,0 +1,22 @@
|
|||
Создадим элемент с прокруткой, но без `border` и `padding`. Тогда разница между его полной шириной `offsetWidth` и внутренней `clientWidth` будет равна как раз прокрутке:
|
||||
|
||||
```js run
|
||||
// создадим элемент с прокруткой
|
||||
var div = document.createElement('div');
|
||||
|
||||
div.style.overflowY = 'scroll';
|
||||
div.style.width = '50px';
|
||||
div.style.height = '50px';
|
||||
|
||||
// при display:none размеры нельзя узнать
|
||||
// нужно, чтобы элемент был видим,
|
||||
// visibility:hidden - можно, т.к. сохраняет геометрию
|
||||
div.style.visibility = 'hidden';
|
||||
|
||||
document.body.appendChild(div);
|
||||
var scrollWidth = div.offsetWidth - div.clientWidth;
|
||||
document.body.removeChild(div);
|
||||
|
||||
alert( scrollWidth );
|
||||
```
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
importance: 3
|
||||
|
||||
---
|
||||
|
||||
# Узнать ширину полосы прокрутки
|
||||
|
||||
Напишите код, который возвращает ширину стандартной полосы прокрутки. Именно самой полосы, где ползунок. Обычно она равна `16px`, в редких и мобильных браузерах может колебаться от `14px` до `18px`, а кое-где даже равна `0px`.
|
||||
|
||||
P.S. Ваш код должен работать на любом HTML-документе, независимо от его содержимого.
|
|
@ -0,0 +1,29 @@
|
|||
Нам нужно создать `div` с такими же размерами и вставить его на место "переезжающего".
|
||||
|
||||
Один из вариантов -- это просто клонировать элемент.
|
||||
|
||||
Если делать это при помощи `div.cloneNode(true)`, то склонируется все содержимое, которого может быть много. Обычно нам это не нужно, поэтому можно использовать `div.cloneNode(false)` для клонирования элемента со стилями, и потом поправить его `width/height`.
|
||||
|
||||
Можно и просто создать новый `div` и поставить ему нужные размеры.
|
||||
|
||||
**Всё, кроме `margin`, можно получить из свойств DOM-элемента, а `margin` -- только через `getComputedStyle`.**
|
||||
|
||||
Причём `margin` мы обязаны поставить, так как иначе наш элемент при вставке будет вести себя иначе, чем исходный.
|
||||
|
||||
Код:
|
||||
|
||||
```js
|
||||
var div = document.getElementById('moving-div');
|
||||
|
||||
var placeHolder = document.createElement('div');
|
||||
placeHolder.style.height = div.offsetHeight + 'px';
|
||||
// можно и width, но в этом примере это не обязательно
|
||||
|
||||
// IE || другой браузер
|
||||
var computedStyle = div.currentStyle || getComputedStyle(div, '');
|
||||
|
||||
placeHolder.style.marginTop = computedStyle.marginTop; // (1)
|
||||
placeHolder.style.marginBottom = computedStyle.marginBottom;
|
||||
```
|
||||
|
||||
В строке `(1)` использование полного название свойства `"marginTop"` гарантирует, что полученное значение будет корректным.
|
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
#moving-div {
|
||||
border: 5px groove green;
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
background-color: yellow;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
Before Before Before
|
||||
|
||||
<div id="moving-div">
|
||||
Text Text Text
|
||||
<br> Text Text Text
|
||||
<br>
|
||||
</div>
|
||||
|
||||
After After After
|
||||
|
||||
<script>
|
||||
var div = document.getElementById('moving-div')
|
||||
|
||||
var placeHolder = document.createElement('div')
|
||||
placeHolder.style.height = div.offsetHeight + 'px'
|
||||
|
||||
var computedStyle = div.currentStyle || getComputedStyle(div, null)
|
||||
|
||||
placeHolder.style.marginTop = computedStyle.marginTop // full prop name
|
||||
placeHolder.style.marginBottom = computedStyle.marginBottom
|
||||
|
||||
// highlight it for demo purposes
|
||||
placeHolder.style.backgroundColor = '#C0C0C0'
|
||||
|
||||
document.body.insertBefore(placeHolder, div)
|
||||
|
||||
div.style.position = 'absolute'
|
||||
div.style.right = div.style.top = 0
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
37
2-ui/1-document/15-size-and-scroll/3-div-placeholder/source.view/index.html
Executable file
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
#moving-div {
|
||||
border: 5px groove green;
|
||||
background-color: yellow;
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
Before Before Before
|
||||
|
||||
<div id="moving-div">
|
||||
Text Text Text
|
||||
<br> Text Text Text
|
||||
<br>
|
||||
</div>
|
||||
|
||||
After After After
|
||||
|
||||
<script>
|
||||
// .. Add your code here
|
||||
|
||||
var div = document.getElementById('moving-div')
|
||||
div.style.position = 'absolute'
|
||||
div.style.right = div.style.top = 0
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
50
2-ui/1-document/15-size-and-scroll/3-div-placeholder/task.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
importance: 3
|
||||
|
||||
---
|
||||
|
||||
# Подменить div на другой с таким же размером
|
||||
|
||||
Посмотрим следующий случай из жизни. Был текст, который, в частности, содержал `div` с зелеными границами:
|
||||
|
||||
```html run no-beautify
|
||||
<style>
|
||||
#moving-div {
|
||||
border: 5px groove green;
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
background-color: yellow;
|
||||
}
|
||||
</style>
|
||||
|
||||
Before Before Before
|
||||
|
||||
<div id="moving-div">
|
||||
Text Text Text<br>
|
||||
Text Text Text<br>
|
||||
</div>
|
||||
|
||||
After After After
|
||||
```
|
||||
|
||||
Программист Валера из вашей команды написал код, который позиционирует его абсолютно и смещает в правый верхний угол. Вот этот код:
|
||||
|
||||
```js
|
||||
var div = document.getElementById('moving-div');
|
||||
div.style.position = 'absolute';
|
||||
div.style.right = div.style.top = 0;
|
||||
```
|
||||
|
||||
Побочным результатом явилось смещение текста, который раньше шел после `DIV`. Теперь он поднялся вверх:
|
||||
|
||||
[iframe height=90 src="source"]
|
||||
|
||||
**Допишите код Валеры, сделав так, чтобы текст оставался на своем месте после того, как `DIV` будет смещен.**
|
||||
|
||||
Сделайте это путем создания вспомогательного `DIV` с теми же `width`, `height`, `border`, `margin`, `padding`, что и у желтого `DIV`.
|
||||
|
||||
Используйте только JavaScript, без CSS.
|
||||
|
||||
Должно быть так (новому блоку задан фоновый цвет для демонстрации):
|
||||
|
||||
[iframe height=140 src="solution"]
|
||||
|
41
2-ui/1-document/15-size-and-scroll/4-put-ball-in-center/ball-half/index.html
Executable file
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
#field {
|
||||
width: 200px;
|
||||
border: 10px groove black;
|
||||
background-color: #00FF00;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#ball {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="field">
|
||||
<img src="https://js.cx/clipart/ball.svg" id="ball"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
var ball = document.getElementById('ball')
|
||||
var field = document.getElementById('field')
|
||||
|
||||
// ball.offsetWidth=0 before image loaded!
|
||||
// to fix: set width
|
||||
ball.style.left = Math.round(field.clientWidth / 2) + 'px'
|
||||
ball.style.top = Math.round(field.clientHeight / 2) + 'px'
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
BIN
2-ui/1-document/15-size-and-scroll/4-put-ball-in-center/field.png
Executable file
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,50 @@
|
|||
При абсолютном позиционировании мяча внутри поля его координаты `left/top` отсчитываются от **внутреннего** угла поля, например верхнего-левого:
|
||||
|
||||

|
||||
|
||||
Метрики для внутренней зоны поля -- это `clientWidth/Height`.
|
||||
|
||||
Центр - это `(clientWidth/2, clientHeight/2)`.
|
||||
|
||||
Но если мы установим мячу такие значения `ball.style.left/top`, то в центре будет не сам мяч, а его левый верхний угол:
|
||||
|
||||
```js
|
||||
var ball = document.getElementById('ball');
|
||||
var field = document.getElementById('field');
|
||||
|
||||
ball.style.left = Math.round(field.clientWidth / 2) + 'px';
|
||||
ball.style.top = Math.round(field.clientHeight / 2) + 'px';
|
||||
```
|
||||
|
||||
[iframe hide="Нажмите, чтобы посмотреть текущий результат" height=180 src="ball-half"]
|
||||
|
||||
Для того, чтобы центр мяча находился в центре поля, нам нужно сместить мяч на половину его ширины влево и на половину его высоты вверх.
|
||||
|
||||
```js
|
||||
var ball = document.getElementById('ball');
|
||||
var field = document.getElementById('field');
|
||||
|
||||
ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px';
|
||||
ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px';
|
||||
```
|
||||
|
||||
**Внимание, подводный камень!**
|
||||
|
||||
Код выше стабильно работать не будет, потому что `IMG` идет без ширины/высоты:
|
||||
|
||||
```html
|
||||
<img src="ball.png" id="ball">
|
||||
```
|
||||
|
||||
**Высота и ширина изображения неизвестны браузеру до тех пор, пока оно не загрузится, если размер не указан явно.**
|
||||
|
||||
После первой загрузки изображение уже будет в кеше браузера, и его размеры будут известны. Но когда браузер впервые видит документ -- он ничего не знает о картинке, поэтому значение `ball.offsetWidth` равно `0`. Вычислить координаты невозможно.
|
||||
|
||||
Чтобы это исправить, добавим `width/height` к картинке:
|
||||
|
||||
```html
|
||||
<img src="ball.png" *!*width="40" height="40"*/!* id="ball">
|
||||
```
|
||||
|
||||
Теперь браузер всегда знает ширину и высоту, так что все работает. Тот же эффект дало бы указание размеров в CSS.
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
#field {
|
||||
width: 200px;
|
||||
border: 10px groove black;
|
||||
background-color: #00FF00;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#ball {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="field">
|
||||
<img src="https://js.cx/clipart/ball.svg" width="40" height="40" id="ball"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
var ball = document.getElementById('ball')
|
||||
var field = document.getElementById('field')
|
||||
|
||||
// ball.offsetWidth=0 before image loaded!
|
||||
// to fix: set width
|
||||
ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px'
|
||||
ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px'
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
#field {
|
||||
width: 200px;
|
||||
border: 10px groove black;
|
||||
background-color: #00FF00;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#ball {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="field">
|
||||
<img src="https://js.cx/clipart/ball.svg" id="ball"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Поместите мяч в центр поля
|
||||
|
||||
Поместите мяч в центр поля.
|
||||
|
||||
Исходный документ выглядит так:
|
||||
|
||||
[iframe src="source" edit link height=180]
|
||||
|
||||
**Используйте JavaScript, чтобы поместить мяч в центр:**
|
||||
|
||||
[iframe src="solution" height=180]
|
||||
|
||||
- Менять CSS нельзя, мяч должен переносить в центр ваш скрипт, через установку нужных стилей элемента.
|
||||
- JavaScript-код должен работать при разных размерах мяча (`10`, `20`, `30` пикселей) без изменений.
|
||||
- JavaScript-код должен работать при различных размерах и местоположениях поля на странице без изменений. Также он не должен зависеть от ширины рамки поля `border`.
|
||||
|
||||
P.S. Да, центрирование можно сделать при помощи чистого CSS, но задача именно на JavaScript. Далее будут другие темы и более сложные ситуации, когда JavaScript будет уже точно необходим, это -- своего рода "разминка".
|
|
@ -0,0 +1,43 @@
|
|||
# Решение через width: auto
|
||||
|
||||
Вначале рассмотрим решение через "умную" установку CSS-свойства.
|
||||
|
||||
Они могут быть разными. Самое простое выглядит так:
|
||||
|
||||
```js
|
||||
elem.style.width = 'auto';
|
||||
```
|
||||
|
||||
Такой способ работает, так как `<div>` по умолчанию распахивается на всю ширину.
|
||||
|
||||
Конечно, такое решение не будет работать для элементов, которые сами по себе не растягиваются, например в случае со `<span>` или при наличии `position: absolute`.
|
||||
|
||||
Обратим внимание, такой вариант был бы неверен:
|
||||
```js
|
||||
elem.style.width = '100%';
|
||||
```
|
||||
|
||||
По умолчанию в CSS ширина `width` -- это то, что *внутри `padding`*, а проценты отсчитываются от ширины родителя. То есть, ставя ширину в `100%`, мы говорим: "внутренняя область должна занимать `100%` ширины родителя". А в элементе есть ещё `padding`, которые в итоге вылезут наружу.
|
||||
|
||||
Можно бы поменять блочную модель, указав `box-sizing` через свойство `elem.style.boxSizing`, но такое изменение потенциально может затронуть много других свойств, поэтому нежелательно.
|
||||
|
||||
# Точное вычисление
|
||||
|
||||
Альтернатива -- вычислить ширину родителя через `clientWidth`.
|
||||
|
||||
Доступную внутреннюю ширину родителя можно получить, вычитая из `clientWidth` размеры `paddingLeft/paddingRight`, и затем присвоить её элементу:
|
||||
|
||||
```js
|
||||
var bodyClientWidth = document.body.clientWidth;
|
||||
|
||||
var style = getComputedStyle(elem);
|
||||
|
||||
*!*
|
||||
var bodyInnerWidth = bodyClientWidth - parseInt(style.paddingLeft) - parseInt(style.paddingRight);
|
||||
*/!*
|
||||
|
||||
elem.style.width = bodyInnerWidth + 'px';
|
||||
```
|
||||
|
||||
Такое решение будет работать всегда, вне зависимости от типа элемента. Конечно, при изменении размеров окна браузера ширина не адаптируется к новому размеру автоматически, как с `width:auto`. Это недостаток. Его, конечно, тоже можно обойти при помощи событий (изучим далее), но как общий рецепт -- если CSS может решить задачу -- лучше использовать CSS.
|
||||
|
53
2-ui/1-document/15-size-and-scroll/5-expand-element/solution.view/index.html
Executable file
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
#elem {
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
background-color: red;
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
/* position: absolute */
|
||||
}
|
||||
|
||||
body {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="elem">
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
текст текст
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var elem = document.getElementById("elem");
|
||||
|
||||
// неверно!
|
||||
//elem.style.width = '100%';
|
||||
|
||||
// верно (так как обычный div по умолчанию растягивается во всю ширину)
|
||||
//elem.style.width = 'auto';
|
||||
|
||||
// верно (решение с JS)
|
||||
var bodyWidth = document.body.clientWidth;
|
||||
|
||||
var style = getComputedStyle(elem);
|
||||
|
||||
var bodyInnerWidth = bodyWidth - parseInt(style.paddingLeft) - parseInt(style.paddingRight);
|
||||
|
||||
elem.style.width = bodyInnerWidth + 'px';
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
39
2-ui/1-document/15-size-and-scroll/5-expand-element/source.view/index.html
Executable file
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
#elem {
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
background-color: red;
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="elem">
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
текст текст
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var elem = document.getElementById("elem");
|
||||
|
||||
// ... ваш код
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
18
2-ui/1-document/15-size-and-scroll/5-expand-element/task.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Расширить элемент
|
||||
|
||||
В `<body>` есть элемент `<div>` с заданной шириной `width`.
|
||||
|
||||
Задача -- написать код, который "распахнет" `<div>` по ширине на всю страницу.
|
||||
|
||||
Исходный документ (`<div>` содержит текст и прокрутку):
|
||||
|
||||
[iframe height=220 src="source"]
|
||||
|
||||
P.S. Пользоваться следует исключительно средствами JS, CSS в этой задаче менять нельзя. Также ваш код должен быть универсален и не ломаться, если цифры в CSS станут другими.
|
||||
|
||||
P.P.S. При расширении элемент `<div>` не должен вылезти за границу `<body>`.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
Отличия:
|
||||
|
||||
1. `getComputedStyle` не работает в IE8-.
|
||||
2. `clientWidth` возвращает число, а `getComputedStyle(...).width` -- строку, на конце `px`.
|
||||
3. `getComputedStyle` не всегда даст ширину, он может вернуть, к примеру, `"auto"` для инлайнового элемента.
|
||||
4. `clientWidth` соответствует внутренней видимой области элемента, *включая `padding`, а CSS-ширина `width` при стандартном значении `box-sizing` соответствует зоне *внутри `padding`*.
|
||||
5. Если есть полоса прокрутки, то некоторые браузеры включают её ширину в `width`, а некоторые -- нет.
|
||||
|
||||
Свойство `clientWidth`, с другой стороны, полностью кросс-браузерно. Оно всегда обозначает размер *за вычетом прокрутки*, т.е. реально доступный для содержимого.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# В чём отличие "width" и "clientWidth" ?
|
||||
|
||||
В чём отличия между `getComputedStyle(elem).width` и `elem.clientWidth`?
|
||||
|
||||
Укажите хотя бы три отличия, лучше -- больше.
|
274
2-ui/1-document/15-size-and-scroll/article.md
Normal file
|
@ -0,0 +1,274 @@
|
|||
# Element size and scrolling
|
||||
|
||||
To show elements at arbitrary places in the page we should:
|
||||
|
||||
1. First, know CSS positioning.
|
||||
2. Second, know how to handle "geometrical" properties in Javascript.
|
||||
|
||||
[cut]
|
||||
|
||||
## Sample element
|
||||
|
||||
For the example we'll use the element with the border, padding and scrolling:
|
||||
|
||||
```html no-beautify
|
||||
<div id="example">
|
||||
...Text...
|
||||
</div>
|
||||
<style>
|
||||
#example {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
border: 25px solid #E8C48F;
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
It has no margins, because they are irrelevant here for us, as they are not the part of the element itself.
|
||||
|
||||
The element looks like this:
|
||||
|
||||

|
||||
|
||||
You can [open the document in the sandbox](sandbox:metric).
|
||||
|
||||
```smart header="Mind the scrollbar"
|
||||
The picture above demonstrates the most complex case when the element has a scrollbar. Some browsers (not all) reserve the space for it by taking it from the content.
|
||||
|
||||
So, without scrollbar the content width would be `300px`, but if the scrollbar is `16px` wide (the width may vary for devices and browsers) then only `300-16 = 284px` remains. Our code should work well if the scrollbar exists and occupies some place, so we consider it the case here.
|
||||
```
|
||||
|
||||
```smart header="The `padding-bottom` may be filled with text"
|
||||
Usually paddings are shown empty on illustrations, but if there's a lot of text in the element and it overflows, then the browsers show it at `padding-bottom`.
|
||||
```
|
||||
|
||||
## Geometry
|
||||
|
||||
Element properties that provide width, height and other geometry are always numbers. They are assumed to be in pixels.
|
||||
|
||||
Here's the overall picture:
|
||||
|
||||

|
||||
|
||||
All properties hardly fit in the picture, but as we'll see soon, their values are simple and easy to understand.
|
||||
|
||||
Let's start exploring them from the outer side of the element.
|
||||
|
||||
## offsetParent, offsetLeft/Top
|
||||
|
||||
These properties are rarely needed. But still they are the "most outer" geometry properties, so we'll start with them.
|
||||
|
||||
The `offsetParent` is the nearest ancestor that is:
|
||||
|
||||
1. CSS-positioned (`position` is `absolute`, `relative` or `fixed`).
|
||||
2. or `<td>`, `<th>`, `<table>`.
|
||||
2. or `<body>`
|
||||
|
||||
The `offsetParent` alone has no use. But `offsetLeft/offsetTop` provide x/y coordinates relative to it's left-upper corner.
|
||||
|
||||
In the example below the inner `<div>` has `<main>` as `offsetParent` and `offsetLeft/offsetTop` are shifts from its left-upper corner (`180`):
|
||||
|
||||
```html run height=10
|
||||
<main style="position: relative" id="main">
|
||||
<article>
|
||||
<div id="example" style="position: absolute; left: 180px; top: 180px">...</div>
|
||||
</article>
|
||||
</main>
|
||||
<script>
|
||||
alert(example.offsetParent.id); // main
|
||||
alert(example.offsetLeft); // 180 (note: a number, not a string "180px")
|
||||
alert(example.offsetTop); // 180
|
||||
</script>
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
There are several occasions when `offsetParent` is `null`:
|
||||
|
||||
1. For not shown elements (`display:none` or not in the document).
|
||||
2. For `<body>` and `<html>`.
|
||||
3. For elements with `position:fixed` on them.
|
||||
|
||||
## offsetWidth/Height
|
||||
|
||||
Now let's move to the element itself.
|
||||
|
||||
These two properties are the simplest ones. They provide the "outer" width/height of the element. Or, in other words, its full size including borders.
|
||||
|
||||

|
||||
|
||||
For our sample element:
|
||||
|
||||
- `offsetWidth = 390` -- the outer width, can be calculated as inner CSS-width (`300px`) plus paddings (`2*20px`) and borders (`2*25px`).
|
||||
- `offsetHeight = 290` -- the outer height.
|
||||
|
||||
````smart header="Geometry properties for not shown elements are zero/null"
|
||||
Geometry properties are calculated only for shown elements.
|
||||
|
||||
If an element (or any of its ancestors) has `display:none` or is not in the document, then `offsetParent` is `null` and `offsetWidth`, `offsetHeight` and other numeric properties are `0`.
|
||||
|
||||
We can use this to check if an element is hidden, like this:
|
||||
|
||||
```js
|
||||
function isHidden(elem) {
|
||||
return !elem.offsetWidth && !elem.offsetHeight;
|
||||
}
|
||||
```
|
||||
|
||||
Should keep in mind that such `isHidden` returns `true` for elements that are on-screen, but have zero sizes (like an empty `<div>`).
|
||||
````
|
||||
|
||||
## clientTop/Left
|
||||
|
||||
Inside the element we have the borders.
|
||||
|
||||
To measure them, there are properties `clientTop` and `clientLeft`.
|
||||
|
||||
In our example:
|
||||
|
||||
- `clientLeft = 25` -- left border width
|
||||
- `clientTop = 25` -- top border width
|
||||
|
||||

|
||||
|
||||
...But to be precise -- they are not borders, but relative coordinates of the inner side from the outer side.
|
||||
|
||||
What's the difference?
|
||||
|
||||
It becomes visible when the document is right-to-left (OS in arabic or hebrew languages). The scrollbar is then not on the right, but on the left, and then `clientLeft` also includes the scrollbar width.
|
||||
|
||||
In that case `clientLeft` in our example would be not `25`, but with the scrollbar width `25+16=41`:
|
||||
|
||||

|
||||
|
||||
## clientWidth/Height
|
||||
|
||||
These properties provide the size of the area inside the element borders.
|
||||
|
||||
They include the content width together with paddings, but without the scrollbar:
|
||||
|
||||

|
||||
|
||||
On the picture above let's first consider `clientHeight`: it's easier to evaluate. There's no horizontal scrollbar, so its exactly the sum of what's inside the borders: CSS-высота `200px` plus top and bottom paddings (`2*20px`) total `240px`.
|
||||
|
||||
Now `clientWidth` -- here the content width is not `300px`, but `284px`, because `16px` are occupied by the scrollbbar. So the sum is `284px` plus left and right paddings, total `324px`.
|
||||
|
||||
**If there are no paddings, then `clientWidth/Height` is exactly the content area, inside the borders and the scrollbar (if any).**
|
||||
|
||||

|
||||
|
||||
So when there's no padding we can use `clientWidth/clientHeight` to get the content area size.
|
||||
|
||||
## scrollWidth/Height
|
||||
|
||||
- Properties `clientWidth/clientHeight` only account for the visible part of the element.
|
||||
- Properties `scrollWidth/scrollHeight` add the scrolled out (hidden) part:
|
||||
|
||||

|
||||
|
||||
On the picture above:
|
||||
|
||||
- `scrollHeight = 723` -- is the full inner height of the content area including the scrolled out part.
|
||||
- `scrollWidth = 324` -- is the full inner width, here we have no horizontal scroll, so it equals `clientWidth`.
|
||||
|
||||
We can use these properties to open the element wide to its full width/height, by the code:
|
||||
|
||||
```js
|
||||
element.style.height = element.scrollHeight + 'px';
|
||||
```
|
||||
|
||||
```online
|
||||
Click the button to open wide the element:
|
||||
|
||||
<div id="element" style="width:300px;height:200px; padding: 0;overflow: auto; border:1px solid black;">text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text</div>
|
||||
|
||||
<button style="padding:0" onclick="element.style.height = element.scrollHeight + 'px'">element.style.height = element.scrollHeight + 'px'</button>
|
||||
```
|
||||
|
||||
## scrollLeft/scrollTop
|
||||
|
||||
Properties `scrollLeft/scrollTop` show how much is hidden behind the scroll. It's the width/height of the hidden, scrolled out part of the element.
|
||||
|
||||
On the picture below we can see `scrollHeight` and `scrollTop` for a block with a vertical scroll:
|
||||
|
||||

|
||||
|
||||
````smart header="`scrollLeft/scrollTop` can be modified"
|
||||
Unlike most other geometry properties that are read-only, `scrollLeft/scrollTop` can be changed, and the browser will scroll the element.
|
||||
|
||||
```online
|
||||
If you click the element below, the code `elem.scrollTop += 10` executes. That makes the element content scroll `10px` below.
|
||||
|
||||
<div onclick="this.scrollTop+=10" style="cursor:pointer;border:1px solid black;width:100px;height:80px;overflow:auto">Click<br>Me<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9</div>
|
||||
```
|
||||
````
|
||||
|
||||
## Don't take width/height from CSS
|
||||
|
||||
We've just covered geometry properties of DOM elements. They are normally used to get widths, heights and distances.
|
||||
|
||||
Now let's see what we should not use.
|
||||
|
||||
As we know from the chapter <info:styles-and-classes>, we can read CSS-height and width using `getComputedStyle`.
|
||||
|
||||
So we can try to read the width of an element like this:
|
||||
|
||||
```js run
|
||||
let elem = document.body;
|
||||
|
||||
alert( getComputedStyle(elem).width ); // show CSS width for elem
|
||||
```
|
||||
|
||||
Why we should use geometry properties instead?
|
||||
|
||||
1. First, CSS `width/height` depend on another property -- `box-sizing` that defines "what is" CSS width and height. A change in `box-sizing` for purposes of CSS may break such JavaScript.
|
||||
2. Second, CSS `width/height` may be `auto`, for instance for an inline element:
|
||||
|
||||
```html run
|
||||
<span id="elem">Hello!</span>
|
||||
|
||||
<script>
|
||||
*!*
|
||||
alert( getComputedStyle(elem).width ); // auto
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
From the CSS standpoint, `width:auto` is perfectly normal, but in JavaScript we need an exact size in `px` that we can use in calculations. So here CSS width is useless at all.
|
||||
|
||||
And there's one more reason. A scrollbar is the reason of many problems. The devil is in the detail. Sometimes the code that works fine without a scrollbar starts to bug with it.
|
||||
|
||||
As we've seen a scrollbar takes the space from the content in some browsers. So the real width available for the content is *less* than CSS width. And `clientWidth/clientHeight` take that into account.
|
||||
|
||||
...But some browsers also take that into account in `getComputedStyle(elem).width`. That is: some of them return real inner width and some of them -- CSS width. Such cross-browser differences is a reason not to use `getComputedStyle`, but rather rely on geometry propeties.
|
||||
|
||||
```online
|
||||
Если ваш браузер показывает полосу прокрутки (например, под Windows почти все браузеры так делают), то вы можете протестировать это сами, нажав на кнопку в ифрейме ниже.
|
||||
|
||||
[iframe src="cssWidthScroll" link border=1]
|
||||
|
||||
У элемента с текстом в стилях указано `width:300px`.
|
||||
|
||||
На момент написания этой главы при тестировании в Chrome под Windows `alert` выводил `283px`, а в Firefox -- `300px`. При этом оба браузера показывали прокрутку. Это из-за того, что Firefox возвращал именно CSS-ширину, а Chrome -- реальную ширину, за вычетом прокрутки.
|
||||
```
|
||||
|
||||
Описанные разночтения касаются только чтения свойства `getComputedStyle(...).width` из JavaScript, визуальное отображение корректно в обоих случаях.
|
||||
|
||||
## Итого
|
||||
|
||||
У элементов есть следующие метрики:
|
||||
|
||||
- `offsetParent` -- "родитель по дереву рендеринга" -- ближайшая ячейка таблицы, body для статического позиционирования или ближайший позиционированный элемент для других типов позиционирования.
|
||||
- `offsetLeft/offsetTop` -- позиция в пикселях левого верхнего угла блока, относительно его `offsetParent`.
|
||||
- `offsetWidth/offsetHeight` -- "внешняя" ширина/высота блока, включая рамки.
|
||||
- `clientLeft/clientTop` -- отступ области содержимого от левого-верхнего угла элемента. Если операционная система располагает вертикальную прокрутку справа, то равны ширинам левой/верхней рамки, если же слева (ОС на иврите, арабском), то `clientLeft` включает в себя прокрутку.
|
||||
- `clientWidth/clientHeight` -- ширина/высота содержимого вместе с полями `padding`, но без полосы прокрутки.
|
||||
- `scrollWidth/scrollHeight` -- ширина/высота содержимого, включая прокручиваемую область. Включает в себя `padding` и не включает полосы прокрутки.
|
||||
- `scrollLeft/scrollTop` -- ширина/высота прокрученной части документа, считается от верхнего левого угла.
|
||||
|
||||
Все свойства, доступны только для чтения, кроме `scrollLeft/scrollTop`. Изменение этих свойств заставляет браузер прокручивать элемент.
|
||||
|
||||
В этой главе мы считали, что страница находится в режиме соответствия стандартам. В режиме совместимости -- некоторые старые браузеры требуют `document.body` вместо `documentElement`, в остальном всё так же. Конечно, по возможности, стоит использовать только режим соответствия стандарту.
|
26
2-ui/1-document/15-size-and-scroll/cssWidthScroll.view/index.html
Executable file
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="elem" style="overflow-y:scroll;width:300px;height:200px;border:1px solid black">
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
текст текст текст текст текст текст текст текст текст текст текст текст текст текст
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// FF: 200, Ch/Sa: 184, Op: 200, IE9: 184, IE8:200
|
||||
</script>
|
||||
|
||||
У элемента стоит <code>style="width:300px"</code>
|
||||
<br>
|
||||
|
||||
<button onclick="alert( getComputedStyle(elem).width )">alert( getComputedStyle(elem).width )</button>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
BIN
2-ui/1-document/15-size-and-scroll/metric-all.png
Normal file
After Width: | Height: | Size: 136 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-all@2x.png
Normal file
After Width: | Height: | Size: 314 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 105 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-client-left-top.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-client-left-top@2x.png
Normal file
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 172 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 141 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-css.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-css@2x.png
Normal file
After Width: | Height: | Size: 165 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-offset-parent.png
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-offset-parent@2x.png
Normal file
After Width: | Height: | Size: 304 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 182 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-scroll-top.png
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
2-ui/1-document/15-size-and-scroll/metric-scroll-top@2x.png
Normal file
After Width: | Height: | Size: 233 KiB |
After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 236 KiB |
94
2-ui/1-document/15-size-and-scroll/metric.view/index.html
Executable file
|
@ -0,0 +1,94 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#example {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
border: 25px solid #E8C48F;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.key {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="example">
|
||||
<h3>Introduction</h3>
|
||||
<p>This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company's Navigator 2.0 browser.
|
||||
It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the
|
||||
Ecma General Assembly of June 1997.</p>
|
||||
|
||||
<p>That Ecma Standard was submitted to ISO/IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep
|
||||
it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.</p>
|
||||
|
||||
<p>The third edition of the Standard introduced powerful regular expressions, better string handling, new control statements, try/catch exception handling, tighter definition of errors, formatting for numeric output and minor changes in anticipation
|
||||
of forthcoming internationalisation facilities and future language growth. The third edition of the ECMAScript standard was adopted by the Ecma General Assembly of December 1999 and published as ISO/IEC 16262:2002 in June 2002.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="mouse-wrap">Координаты мыши: <span id="mouse">...</span></div>
|
||||
<div id="info"></div>
|
||||
|
||||
|
||||
<script>
|
||||
var example = document.getElementById('example')
|
||||
|
||||
var info = document.getElementById('info')
|
||||
|
||||
var props = {
|
||||
'размеры': ['clientLeft', 'clientTop', 'clientWidth', 'clientHeight', 'offsetWidth', 'offsetHeight', 'scrollWidth', 'scrollHeight'],
|
||||
'прокрутка': ['scrollLeft', 'scrollTop'],
|
||||
'позиционирование по рендерингу': ['offsetParent', 'offsetLeft', 'offsetTop']
|
||||
}
|
||||
|
||||
info.innerHTML = '<h3>Нажмите для просмотра значения:</h3>';
|
||||
for (var k in props) {
|
||||
info.innerHTML += '<h4>' + k + '</h4>';
|
||||
var prop = props[k];
|
||||
for (var i = 0; i < prop.length; i++) {
|
||||
info.innerHTML += "<span class='key'>" + prop[i] + '</span>: <span id="' + prop[i] + '"> </span>' + " "
|
||||
i++;
|
||||
if (i < prop.length) {
|
||||
info.innerHTML += "<span class='key'>" + prop[i] + '</span>: <span id="' + prop[i] + '"> </span>';
|
||||
}
|
||||
info.innerHTML += "<br/>";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
document.onclick = function(event) {
|
||||
var target = event.target;
|
||||
if (!target.classList.contains('key')) return;
|
||||
|
||||
var prop = target.innerHTML;
|
||||
var value = example[prop];
|
||||
value = value.tagName || value;
|
||||
document.getElementById(prop).innerHTML = value;
|
||||
};
|
||||
|
||||
|
||||
document.onmousemove = function(e) {
|
||||
document.getElementById('mouse').innerHTML = Math.round(e.clientX) + ':' + Math.round(e.clientY);
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|