【问题标题】:How to create a background over non-sibling DOM nodes?如何在非同级 DOM 节点上创建背景?
【发布时间】:2021-01-10 03:18:28
【问题描述】:

所以我有一个很好地组织的网页:

<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 组织,因为它是语义化的。

【问题讨论】:

  • 元素的高度是恒定的吗? (在这种情况下30vh,或者在媒体查询的情况下任何其他常量值)
  • @Louis Coulet:我想出了一种解决方案来解决您的问题。我也很喜欢在这方面工作。如果我的回答可以帮助您接受它并投票支持其他开发人员以获得帮助。 :) 谢谢

标签: html css


【解决方案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 hack 以使您的 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 底部的总高度。

1pxheaderborder-bottom
30vhsection 的恒定高度,
2px 是它的border-topborder-bottom .

Javascript

如果您希望保留原始层次结构并需要 section 可变大小或间隔,您可以诉诸 javascript 来监听窗口大小的变化并将 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 进行 polyfill 或编译)

【讨论】:

  • 谢谢您,您的 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>

【讨论】:

  • 谢谢您,您的解决方案有效!在 javascript 中使用 getBoundingClientRect 是个好主意。
  • 虽然你是最快的并且你的回答是正确的,但我必须接受@soimon 的回答,因为它提供了 3 种选择。非常感谢您抽出宝贵时间,再见。
  • @LouisCoulet:亲爱的朋友!我是来帮忙的……你甚至可以投反对票……这并不重要。只是一个建议......职位:绝对;并且 header:pseudo 解决方案不是一个好的解决方案.. 避免它... 休息很好 :) 它可能会在您的代码中搞砸.. 当您说需要导航栏和子菜单时,管理 z-indexes 是额外的负载:)
猜你喜欢
  • 2017-02-01
  • 2021-05-10
  • 2013-11-25
  • 1970-01-01
  • 2017-01-27
  • 1970-01-01
  • 2012-12-25
  • 2022-12-26
  • 1970-01-01
相关资源
最近更新 更多