#html #css
#HTML #css
Вопрос:
Итак, у меня есть веб-страница, хорошо организованная как таковая:
<html>
<head>...</head>
<body>
<header>header</header>
<main style="max-width: 80vw; margin: 0 auto;">
<section>section 1</section>
<section>section 2</section>
<section>section 3</section>
<section>section 4</section>
</main>
<footer>footer</footer>
</body>
</html>
Я хотел бы иметь фон с линейным градиентом, охватывающий заголовок и первый раздел.
Это вызывает две проблемы:
- как создать элемент, который охватывает неродственные DOM-узлы?
- как создать элемент, который охватывает ограниченную ширину первого раздела?
В идеале я хотел бы сохранить свою организацию HTML, потому что она семантическая.
Комментарии:
1. Постоянны ли высоты элементов? (В этом случае
30vh
или в случае медиа-запросов любое другое постоянное значение)2. @Louis Coulet: Я придумал одно решение для вашей проблемы. Мне также понравилось работать над этим. Если мой ответ поможет вам, примите его и проголосуйте за него, чтобы другие разработчики могли получить помощь. 🙂 Спасибо
Ответ №1:
как создать элемент, который охватывает неродственные DOM-узлы?
Вы не можете. Действительный HTML требует иерархии только с одним прямым родителем (в вашем гипотетическом примере у первого section
будет два: main
и новый с градиентом).
Если вы хотите добиться этого эффекта, у вас есть несколько вариантов:
Измените свою иерархию
Вы могли бы сгладить свою иерархию в
<div class="gradient-wrapper">
<header>header</header>
<section>section 1</section>
</div>
<section>section 2</section>
<section>section 3</section>
<section>section 4</section>
и наложите оригинальный стиль main
на разделы, используя
section {
max-width: 80vw;
margin: 0 auto;
}
.gradient-wrapper {
background: linear-gradient(...);
}
Это самый простой метод, поскольку по дизайну он реагирует по вертикали: градиент автоматически растягивается, чтобы охватить его дочерние элементы.
Постоянная высота с помощью CSS
Если высота и расстояние до вашего первого узла section
известны и постоянны, вы можете развернуть CSS-хак, чтобы сделать фон вашего header
узла точно таким же, как ваш section
(без изменения исходной иерархии).
header {
position: relative;
}
header::before {
display: block;
width: 100%;
content: "";
position: absolute;
z-index: -1;
--first-section-height: (1px 30vh 2px);
height: calc(100% var(--first-section-height));
background: linear-gradient(...);
}
Для ясности я создал --first-section-height
переменную, которая представляет общую высоту от нижней части вашего header
до нижней части вашего section
.
1px
является border-bottom
ли header
,
30vh
является ли постоянная высота вашего section
и
2px
является ли его border-top
и border-bottom
.
Javascript
Если вы хотите сохранить исходную иерархию и хотите section
, чтобы она имела переменный размер или интервал, вы можете прибегнуть к javascript, чтобы отслеживать изменения в размере окна и позиционировать a div
в объединенных ограничивающих рамках двух элементов. Минимальный рабочий пример такого поведения:
window.addEventListener("load", updateGradientBox);
window.addEventListener("resize", updateGradientBox);
updateGradientBox();
function updateGradientBox() {
let header = document.querySelector("header");
let section = document.querySelector("main section:first-child");
let gradient = document.querySelector("#gradient");
if (header === null || section === null || gradient === null) return;
let rects = [header, section].map((el) => el.getBoundingClientRect());
let rect = getCombinedBoundingRect(rects);
gradient.style.top = `${rect.top}px`;
gradient.style.left = `${rect.left}px`;
gradient.style.width = `${rect.width}px`;
gradient.style.height = `${rect.height}px`;
}
function getCombinedBoundingRect(rects) {
const r = {
top: Math.min(...rects.map((r) => r.top)),
left: Math.min(...rects.map((r) => r.left)),
bottom: Math.max(...rects.map((r) => r.bottom)),
right: Math.max(...rects.map((r) => r.right))
};
return {
...r,
width: r.right - r.left,
height: r.bottom - r.top
};
}
#gradient {
position: absolute;
display: block;
z-index: -1;
background: linear-gradient(rgba(217, 217, 217, 1) 0%, rgba(85, 85, 85, 1) 100%);
}
<div id="gradient"></div>
<header>header</header>
<main>
<section>section 1</section>
<section>section 2</section>
<section>section 3</section>
<section>section 4</section>
</main>
<footer>footer</footer>
Преимущество этого метода в том, что он адаптируется к любому мыслимому размеру и положению (о чем свидетельствует мой минимальный пример, который вообще не представляет предлагаемый вами макет).
Полный рабочий пример можно найти на
(Пожалуйста, имейте в виду, что в моем примере я использую новейший синтаксис javascript и API; в зависимости от желаемой поддержки старых браузеров вы можете захотеть заполнить или скомпилировать его с помощью Babel)
Комментарии:
1. Спасибо, ваши 3 решения работают, вы четко объясняете различные подходы. Теперь у всех 3 есть недостатки: первый портит семантическую иерархию, второй требует CSS-вычислимых высот, третий вводит javascript для стилизации. Мне нравится ваша функция getCombinedBoundingRect!
Ответ №2:
Я придумал это решение, используя javascript. Надеюсь, я правильно понял проблему 🙂
Основная идея состоит в том, чтобы динамически получать точные высоты первого header
и первого section
и устанавливать градиент на теле до этой высоты.
Codepen: https: //codesandbox.io/s/the-gradient-stuff-h3yx1
const header = document.querySelector("header");
const section1 = document.querySelector("main section:first-child");
const headerHeight = header.getBoundingClientRect().height;
const section1Height = section1.getBoundingClientRect().height;
let totalHeight = headerHeight section1Height;
console.log(totalHeight);
const body = document.querySelector("body");
console.log(body);
body.style.backgroundImage = `linear-gradient(to bottom, #33ccff 0%, #ff99cc ${totalHeight}px)`;
body.style.backgroundSize = `100% ${totalHeight}`;
body.style.backgroundRepeat = "no-repeat";
body.style.backgroundSize = `100% ${totalHeight}px`;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
header,
footer,
section {
height: 30vh;
border: 1px solid black;
}
</style>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<header>header</header>
<main style="max-width: 80vw; margin: 0 auto;">
<section>section 1</section>
<section>section 2</section>
<section>section 3</section>
<section>section 4</section>
</main>
<footer>footer</footer>
<script src="./script.js"></script>
</body>
</html>
Комментарии:
1. Спасибо, ваше решение работает! Хорошая идея использовать getBoundingClientRect в javascript.
2. Хотя вы были самым быстрым, и ваш ответ точен, я должен принять ответ @soimon, поскольку он предоставляет 3 альтернативы. Большое вам спасибо за уделенное время, до встречи.
3. @LouisCoulet : Дорогой друг ! Я здесь, чтобы помочь… Вы можете даже проголосовать против… Вряд ли это имеет значение. Но это всего лишь совет… Позиция: абсолютная; и заголовок: псевдо-решение не является хорошим.. Избегайте этого… Остальное в порядке 🙂 Это может что-то испортить позже в вашем коде.. Когда вы говорите, что нужна панель навигации и подменю, то управление z-индексами — это дополнительная нагрузка 🙂