zhangxinxu / quiz

小测答题收集区
535 stars 43 forks source link

DOM基础测试43期 #62

Open zhangxinxu opened 4 years ago

zhangxinxu commented 4 years ago

本期题目也是源自实际项目,关于尺寸与定位:

原图地址

大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。

```js
// 你的JS代码写在这里
 ```

其他 本期小测没有直播,也没有打分,但是会反馈要点。

liyongleihf2006 commented 4 years ago

jsbin

.header {
    height: 300px;
    background: lightgray;
}
.content {
    position: relative;
    writing-mode: vertical-lr;
    width:100%;
}
.nav-wrap{
    writing-mode:horizontal-tb;
    display: inline-block;
    margin-top:-50%;
    width:100%;
    text-align: center;
}
.nav {
    display:inline-block;
    border: 10px solid #53bff5;
    border-radius: 10px;
}
.other{
    display: inline-block;
    position:absolute;
    writing-mode:horizontal-tb;
    width:100%;
}
.crumb-ol {
    display:flex;
    flex-wrap: wrap;
    margin: 0;
    padding-left: 0;
    list-style: none;
}
.crumb{
    padding:20px;
}
.crumb-img {
    display:block;
    height: 60px;
    width: 40px;
    margin:auto;
    margin-bottom: 20px;
    background:lightgreen;
}
<div class="header"></div>
<div class="content">
    <div class="nav-wrap">
        <nav class="nav">
            <ol class="crumb-ol">
                <li class="crumb">
                    <img class="crumb-img" />
                    <span class="crumb-text">进口食品</span>
                </li>
                <li class="crumb">
                    <img class="crumb-img" />
                    <span class="crumb-text">进口食品</span>
                </li>
                <li class="crumb">
                    <img class="crumb-img" />
                    <span class="crumb-text">进口食品</span>
                </li>
                <li class="crumb">
                    <img class="crumb-img" />
                    <span class="crumb-text">进口食品</span>
                </li>
            </ol>
        </nav>
    </div>
    <div class="other">其他元素需要跟着一起往上挪</div>
</div>
LuckyRabbitFeet commented 4 years ago
<div style="height: 200px;background: red;"></div>
<nav id="nav" class="nav"></nav>
<div style="height: 200px;background: red;"></div>
.nav {
  display: flex;
  flex-wrap: wrap;
  width: 200px;
  background: rgb(70, 119, 253);
}

.nav div {
  padding: 5px;
}
const nav = document.getElementById("nav");

for (let i = 0; i < 10; i++) {
  const div = document.createElement("div");
  div.innerHTML = "test";
  nav.appendChild(div);
}

nav.style.marginTop = `-${nav.offsetHeight / 2}px`;

demo

guqianfeng commented 4 years ago
    <header></header>
    <nav id="nav" class="nav"></nav>
    <footer></footer>
        div{
            background-color: red;
        }
        header, nav, footer{
            height: 10rem;
        }
        nav{
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            grid-gap: 1rem;
        }
        {
            render(9)
            function render(num){
                let navEl = document.getElementById("nav");
                let inner = [...".".repeat(num)].map(item => "<div></div>").join("");
                navEl.innerHTML = inner;
                navEl.style.marginTop = `-${navEl.offsetHeight / 2}px`;
            }
        }
Despair-lj commented 4 years ago

demo

<nav class="nav" id="nav">
  <ul class="nav-wrap">
    <li class="nav-wrap-item">
      <i class="nav-wrap-item-icon"></i>
      <span class="nav-wrap-item-title">进口食品</span>
    </li>
    <li class="nav-wrap-item">
      <i class="nav-wrap-item-icon"></i>
      <span class="nav-wrap-item-title">进口食品</span>
    </li>
    <li class="nav-wrap-item">
      <i class="nav-wrap-item-icon"></i>
      <span class="nav-wrap-item-title">进口食品</span>
    </li>
    <li class="nav-wrap-item">
      <i class="nav-wrap-item-icon"></i>
      <span class="nav-wrap-item-title">进口食品</span>
    </li>
    <li class="nav-wrap-item">
      <i class="nav-wrap-item-icon"></i>
      <span class="nav-wrap-item-title">进口食品</span>
    </li>
    <li class="nav-wrap-item">
      <i class="nav-wrap-item-icon"></i>
      <span class="nav-wrap-item-title">进口食</span>
    </li>
  </ul>
</nav>
<div id="navTop"></div>
body {
  margin: 0;
  font-family: sans-serif;
  font-weight: 400;
  font-size: 16px;
}
.nav {
  margin:0 20px;
  transform: translateY(-50%);
}
.nav-wrap {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  margin: 0;
  padding: 0;
  border: 10px solid rgb(80, 190,250);
  border-radius: 10px;
  list-style: none;
  background-color: #fff;
}
.nav-wrap-item {
  flex-basis: 65px;
  margin: 20px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;   
}
.nav-wrap-item-icon {
  display: block;
  width: 100%;
  padding-top: 100%;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' class='octicon octicon-mark-github text-white' aria-hidden='true' viewBox='0 0 16 16' width='32' height='32'%3E%3Cpath fill-rule='evenodd' d='M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z'/%3E%3C/svg%3E") no-repeat center;
  background-size: 100%;
  margin: 0 0 20px;
}
var nav = document.querySelector('.nav');
var navTop = document.querySelector('#navTop');
function resize() {
  var navHalfHeight = parseFloat(getComputedStyle(nav).height) / 2;
  if (parseFloat(navTop.style.marginTop) !== -navHalfHeight) {
    navTop.style.marginTop = -navHalfHeight + 'px';
  }
}
resize()
window.addEventListener('resize', resize);
ziven27 commented 4 years ago

查看demo : JSBIN

我的理解是,本题就是一个不定高的元素怎么往上移动高度的50%,且后续元素跟随。

能想到的思路是利用writing-mode: vertical-lr;margin-top 基于高度去计算。可是发现margin-top 是百分比的时候元素并不会跟随,可是换成同样数值的固定值就可以。 不是很理解这个逻辑。所以并未实现。

看到 @liyongleihf2006 用 position:absolute; 的解法我震惊了,手动为他点赞

.banner {
     padding-top: 30%;
    background-color: rgba(255, 0, 0, 0.5);
}
.nav {
    background-color: blue;
    width: 92%;
    margin: 0 auto;
    writing-mode: vertical-lr;
}
.nav-in{
    display: inline-block;
    width: 100%;
    background-color: rgba(0,255, 0, 0.5);
    height: 80px;
}

.nav-in{ 
  margin-top: -50%;  /* 不理解为啥后面元素不跟随 */
}
#nav2 .nav-in{ 
  margin-top: -40px; /* 这样就可以 */
}
  <section class="banner"></section>
  <nav id="nav" class="nav">
    <div class="nav-in"></div>
  </nav>
  我是其它的文案1
  <section class="banner"></section>
  <nav id="nav2" class="nav">
    <div class="nav-in"></div>
  </nav>
  我是其它的文案2
LuckyRabbitFeet commented 4 years ago

@ziven27 margin当单位是%的时候是相对于包含快的宽度来进行计算的,也就是说

.nav-in{ 
  margin-top: -50%;  /* 不理解为啥后面元素不跟随 */
}

里面的margin-top实际是按照.nav的宽度来进行计算的。

又因为.nav使用了writing-mode: vertical-lr;属性,所以实际值是按照.nav的高度来进行计算的。而.nav的高度是被.nav-in撑开的,所以.nav是有一个高度值的,这个值就是.nav-in的高度。

最终即使.nav-in因为margin-top上移了,之后的其他文案也没有跟随,因为.nav已经有了一个高度占位了。

拙见,不知道我理解的对不对。

xxf1996 commented 4 years ago

第一眼还以为是CSS基础测试题,结果就奔着纯CSS去了;想半天卡壳了,回来一看居然是DOM测试题……

@后话:本来以为纯CSS没有办法,评论区果然打脸了;虽然之前也试过改变writing-mode,让margin-top能够取到50%的高度,但是好像不是那么直接,看了 @liyongleihf2006d 的答案才知道原来如此

demo

<div class="banner">
  <img class="banner-item" src="https://placem.at/things?w=375&h=200">
</div>
<nav id="channel">
  <a class="channel-item">
    <img class="channel-icon" src="https://placem.at/things?w=80&h=80">
    <p class="channel-title">推荐</p>
  </a>
  <a class="channel-item">
    <img class="channel-icon" src="https://placem.at/things?w=80&h=80">
    <p class="channel-title">零食</p>
  </a>
  <a class="channel-item">
    <img class="channel-icon" src="https://placem.at/things?w=80&h=80">
    <p class="channel-title">狗粮</p>
  </a>
</nav>
<div class="activity">
  <img class="activity-item" src="https://placem.at/things?w=200&h=120">
  <img class="activity-item" src="https://placem.at/things?w=200&h=120">
  <img class="activity-item" src="https://placem.at/things?w=200&h=120">
  <img class="activity-item" src="https://placem.at/things?w=200&h=120">
</div>
<div id="plus">+1</div>
html,
body {
  padding: 0;
  margin: 0;
}
body {
  max-width: 375px;
}
.banner {
  font-size: 0;
}
.banner-item {
  width: 100%;
}
#channel {
  position: relative;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
  width: fit-content;
  max-width: 355px;
  margin: 0 auto;
  background-color: #f9f9f9;
  border-radius: 10px;
  border: 1px solid #ccc;
}
.channel-item {
  width: 40px;
  height: 60px;
  margin: 10px;
}
.channel-icon {
  display: block;
  width: 40px;
  height: 40px;
}
.channel-title {
  margin: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
}
.activity {
  box-sizing: border-box;
  display: flex;
  flex-flow: row wrap;
}
.activity-item {
  width: 44%;
  margin: 10px 3%;
  border-radius: 8px;
}
#plus {
  position: absolute;
  top: 20px;
  left: 20px;
  width: 40px;
  height: 40px;
  line-height: 40px;
  text-align: center;
  color: #333;
  background: lightcoral;
  box-shadow: 0 0 5px 0 lightgreen;
}
let channel = document.getElementById('channel')
let plus = document.getElementById('plus')

function moveChannel () {
  channel.style.marginTop = `-${channel.clientHeight / 2}px`
}

moveChannel()

function addItem () {
  channel.appendChild(channel.querySelector('a.channel-item').cloneNode(true))
  moveChannel()
}

plus.addEventListener('click', addItem)
livetune commented 4 years ago

Demo 当高度变化时,使用js赋值marginTop 为自身高度的一半

resizeMargin();
function resizeMargin() {
  nav.style.marginTop = -0.5 * nav.clientHeight + 'px';
}
const observer = new MutationObserver(function() {
  resizeMargin();
});

observer.observe(nav, {
  childList: true, // 子节点的变动(新增、删除或者更改)
  attributes: true, // 属性的变动
  characterData: true, // 节点内容或节点文本的变动
  subtree: true // 是否将观察器应用于该节点的所有后代节点
});
zhangxinxu commented 4 years ago

@liyongleihf2006 可以可以,溜溜溜。我本以为这题CSS是无解的,冒昧问下现在在哪里高就?

本期要点:

  1. margin-top负1/2高度;
  2. 记得加resize事件;
  3. (很重要)相关JS代码要在页面中内联,并写在
zer0fire commented 4 years ago

Demo

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- nav里面的导航的个数不固定,可能1行,也可能2行,宽度也是弹性变化的,
    请实现nav元素无论里面内容如何变化,都往上偏移1/2自身的高度(注:后面的布局也跟着往上移动) -->
  <title>DOM43</title>
  <style>
    h2 {
      margin: 0;
      background-color: lightblue;
      height: 300px;
    }
    #nav {
      text-align: center;
    }
    .container {
      display: inline-block;
    }
    .item-container {
      margin: 0;
      padding: 0;
      list-style: none;
      display: flex;
      justify-content: space-around;
      align-content: space-around;
      box-shadow: 0 0 0 10px lightseagreen, 0 0 0 11px black;
      border-radius: 20px;
      flex-flow: wrap;
    }
    .item {
      margin: 10px;
    }
    footer{
      margin: 0;
      background-color: lightblue;
      height: 300px;
    }
  </style>
</head>
<body>
  <h2>上面的Banner</h2>
  <nav id="nav" class="nav">
    <div class="container">
      <ul class="item-container" >
        <li class="item" >
          <img src="https://via.placeholder.com/150" alt="">
        </li>
        <li class="item" >
          <img src="https://via.placeholder.com/150" alt="">
        </li>
        <li class="item" >
          <img src="https://via.placeholder.com/150" alt="">
        </li>
        <li class="item" >
          <img src="https://via.placeholder.com/150" alt="">
        </li>
        <li class="item" >
          <img src="https://via.placeholder.com/150" alt="">
        </li>
        <li class="item" >
          <img src="https://via.placeholder.com/150" alt="" id="lastImg">
        </li>
      </ul>
    </div>
  </nav>
  <footer>
    下面的布局
  </footer>
</body>
<script>
  function resize() {
    nav.style.marginTop = -0.5 * nav.clientHeight + 'px'
  }
  resize()
  const observer = new MutationObserver(function() {
    resize()
  })
  observer.observe(nav, {
    childList:true,
    attibutes: true,
    charactorData: true,
    subtree: true,
  })
  window.addEventListener('resize', resize)
  lastImg.addEventListener('load', resize)
</script>
</html>
simplefeel commented 4 years ago

@ziven27 margin当单位是%的时候是相对于包含快的宽度来进行计算的,也就是说

.nav-in{ 
  margin-top: -50%;  /* 不理解为啥后面元素不跟随 */
}

里面的margin-top实际是按照.nav的宽度来进行计算的。

又因为.nav使用了writing-mode: vertical-lr;属性,所以实际值是按照.nav的高度来进行计算的。而.nav的高度是被.nav-in撑开的,所以.nav是有一个高度值的,这个值就是.nav-in的高度。

最终即使.nav-in因为margin-top上移了,之后的其他文案也没有跟随,因为.nav已经有了一个高度占位了。

拙见,不知道我理解的对不对。

基本是这个理解,所以最终要想底部元素往上跟随,故需要将跟随元素(必须是display:inline-block)和(进行了margin-top:-50%)元素在同一级,然后因为共同父元素 writing-mode: vertical-lr,利用 文本元素从上到下实现元素跟随

ziven27 commented 4 years ago

@LuckyRabbitFeet @simplefeel

margin当单位是%的时候是相对于包含快的宽度来进行计算的,也就是说

.nav-in{ 
  margin-top: -50%;  /* 不理解为啥后面元素不跟随 */
}

里面的margin-top实际是按照.nav的宽度来进行计算的。

又因为.nav使用了writing-mode: vertical-lr;属性,所以实际值是按照.nav的高度来进行计算的。而.nav的高度是被.nav-in撑开的,所以.nav是有一个高度值的,这个值就是.nav-in的高度。

最终即使.nav-in因为margin-top上移了,之后的其他文案也没有跟随,因为.nav已经有了一个高度占位了。

拙见,不知道我理解的对不对。

是的,我也是这样想的。

可是问题在于,这个 margin-top:-50% 换成 margin-top:-40px (这两个值是一样的),后面的元素就可以跟随了。

LuckyRabbitFeet commented 4 years ago

@LuckyRabbitFeet @simplefeel

margin当单位是%的时候是相对于包含快的宽度来进行计算的,也就是说

.nav-in{ 
  margin-top: -50%;  /* 不理解为啥后面元素不跟随 */
}

里面的margin-top实际是按照.nav的宽度来进行计算的。 又因为.nav使用了writing-mode: vertical-lr;属性,所以实际值是按照.nav的高度来进行计算的。而.nav的高度是被.nav-in撑开的,所以.nav是有一个高度值的,这个值就是.nav-in的高度。 最终即使.nav-in因为margin-top上移了,之后的其他文案也没有跟随,因为.nav已经有了一个高度占位了。 拙见,不知道我理解的对不对。

是的,我也是这样想的。

可是问题在于,这个 margin-top:-50% 换成 margin-top:-40px (这两个值是一样的),后面的元素就可以跟随了。

@ziven27 当你使用margin-top:-40px时,nav的高度就是子元素高度了,子元素高度为.nav-in本身的高度减去40px

wingmeng commented 4 years ago
  1. 需要 JS 加持的方案  > 在线 Demo <

    
    /* (仅包含关键代码)*/
    .nav {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
    gap: 10px;
    }

.nav-item-img > img { display: block; width: 100%; max-width: 120px; margin: auto; }


```html
<header class="header-banner"></header>
<nav id="nav" class="nav">
  <a href="#" class="nav-item">
    <div class="nav-item-img">
      <img src="https://static.easyicon.net/preview/123/1234470.gif" alt="甜甜圈">
    </div>
    <div class="nav-item-text">甜甜圈</div>
  </a>
  <!-- 略... -->
</nav>
<div>其他内容其他内容其他内容其他内容其他内容其他内容其他内容其他内容</div>
const navEl = document.getElementById('nav');

// 图像载入后再取容器高度,避免高度获取不准确。缺点是首次载入会抖动
window.addEventListener('load', fixTop);
window.addEventListener('resize', fixTop);

function fixTop() {
  navEl.style.marginTop = -navEl.offsetHeight / 2 + 'px';
}
  1. 纯 CSS 方案  > 在线 Demo <

    要想让元素偏移后,其他临近元素自动补上空出的位置,貌似只有 margin 有这个特性,但默认情况下 margin 的垂直方向百分比属性值是以元素宽度为基准的,这就无法利用自身高度来进行百分比偏移了。好在有 writing-mode张老师博客传送门)属性,结合 @liyongleihf2006 的精彩示例,我也实现了一版。

/* (仅包含关键代码)*/
.container {
  width: 100%;
  writing-mode: vertical-lr;
}
.container > * {
  width: 100%;
  writing-mode: horizontal-tb;
}

.nav {
  float: left;
  width: calc(100% - 15px * 2);
  margin: -50% 15px 0;
}
<!-- 为了消除 writing-mode 的影响,html 结构多套了两层 -->
<header class="header-banner"></header>
<main class="container">
  <nav id="nav" class="nav">
    <div class="nav-inner">
      <a href="#" class="nav-item">
        <div class="nav-item-img">
          <img src="https://static.easyicon.net/preview/123/1234470.gif" alt="甜甜圈">
        </div>
        <div class="nav-item-text">甜甜圈</div>
      </a>
    <!-- 略... -->      
    </div>
  </nav>
  <div>其他内容其他内容其他内容其他内容其他内容其他内容其他内容其他内容</div>
</main>