【问题标题】:How to make an HTML element to expand on hover and overlay similar neighbor elements in the same flex container without moving them?如何使 HTML 元素在悬停时展开并在同一个 flex 容器中覆盖相似的相邻元素而不移动它们?
【发布时间】:2019-12-26 22:50:29
【问题描述】:

有N张相同类别的卡片,每张卡片里面的文字数量不同。所有卡片都在同一个弹性容器中(行、换行)。在某些卡片中,部分文本是隐藏的,因为它超出了类的尺寸限制。我希望此类卡片在鼠标悬停时展开并显示全部内容。到目前为止,我可以使其仅覆盖文本(如图像中所示)或在移动邻居时扩展。相反,这样的卡片应该覆盖在相邻卡片之上而不移动它们。

理想情况下,悬停的卡片应水平展开(如果可能,对称展开,否则仅向左或向右)和垂直向下展开。

这是为这个问题创建的一个非常简单的示例。每张卡片都包含一个随机长度的子串 lorem ipsum

JavaScript,生成带有随机文本数量的卡片:

'use strict';

const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ac auctor augue mauris augue neque gravida in fermentum et. Accumsan in nisl nisi scelerisque. Sed pulvinar proin gravida hendrerit lectus. Tortor id aliquet lectus proin nibh nisl condimentum. Erat pellentesque adipiscing commodo elit at imperdiet dui accumsan sit.';

const items = Array(30).fill(0)
  .map(() => Math.ceil(lorem.length * Math.random()))
  .map(idx => lorem.substring(0, idx))
  .map(txt => `<div class="lorem-card">${txt}</div>`)
  .join('');

const html = `<div class="cards-container">${items}</div>`;
document.body.innerHTML += html;

html只是一个骨架,因为内容是由JavaScript生成的

<html>
  <head>
    <meta charset="utf-8">
    <title>Cards demo</title>
    <link rel="stylesheet" type="text/css" href="./cards.css">
  </head>
  <body>
  </body>
  <script src="./gen.js"></script>
</html>

这是当前的 CSS:

.cards-container {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  align-content: space-between;
}

.lorem-card {
  flex-grow: 1;
  background-color: white;
  border: 3px solid #008CBA;
  padding: 15px;
  margin: 15px;
  max-width: 150px;
  max-height: 200px;
  overflow: hidden;
  opacity: 1;
}

.lorem-card:hover {
  flex-grow: 10;
  height: auto;
  width: auto;
  overflow: visible;
  border: 3px solid red;
  z-index: 100;
}

https://jsfiddle.net/fpmuc5Lz/3/

【问题讨论】:

  • 你试过 max-height: 100% on hover 吗?
  • 我真的不会在悬停时执行此操作,除非您要通过 JS 放置叠加层,或者克隆节点并将其完全放在顶部。否则你会在每个场景中不断闪烁。
  • 一个附带问题:投票结束这个问题的原因是什么?
  • 我的回答能回答你的问题吗?

标签: javascript html css flexbox overflow


【解决方案1】:

到目前为止,这就是我得出的结论:在覆盖具有初始非绝对定位元素的元素时,不可能不导致其他 divs 移动。因此,我决定创建一个绝对定位的复制元素,其宽度与原始元素完全相同。这是我在您的 JS 中更改的内容:

'use strict';

const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ac auctor augue mauris augue neque gravida in fermentum et. Accumsan in nisl nisi scelerisque. Sed pulvinar proin gravida hendrerit lectus.';

const items = Array(30).fill(0)
  .map(() => Math.ceil(lorem.length * Math.random()))
  .map(idx => lorem.substring(0, idx))
  .map(txt => `<div class="lorem-card"><div class="lorem-card--real">${txt}</div><div class="lorem-card--substitute">${txt}</div></div>`)
  .join('');

const html = `<div class="cards-container">${items}</div>`;
document.body.innerHTML += html;

还有你的 CSS:

.lorem-card {
  position: relative;
  flex-grow: 1;
  max-width: 150px;
  max-height: 200px;
  margin: 15px;
  overflow: hidden;
}

.lorem-card--real {
  max-width: 150px;
  max-height: 200px;
  padding: 15px;
  border: 3px solid #008CBA;
  background: #FFF;
  text-overflow: ellipsis;
}

.lorem-card--substitute {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 15px;
  background: #FFF;
  border: 3px solid #008CBA;
}

.lorem-card:hover {
  overflow: visible;
}

.lorem-card:hover .lorem-card--real {
  opacity: 0;
}

.lorem-card:hover .lorem-card--substitute{
  height: auto;
  overflow: visible;
  border: 3px solid red;
  z-index: 100;
}

这个想法是为两个Lorem cards 创建一个容器。一种具有设定的宽度和高度(最大为 200 像素和 150 像素),一种可以动态扩展并覆盖其他元素而不会导致其他 divs 移动(通过使用 position: absolute)。可以看出,效果是绝对定位的元素只垂直向下扩展。此外,由于height 设置为auto,CSS 过渡不起作用,这导致了以下建议。

我目前的想法,如果你想让它在水平方向和垂直向下扩展,是计算整个字符串的 total 宽度(排列在一条水平线上)和最大的高度特点。然后,使用一些数学将 div 的宽度和高度比限制为 4:3(同时处理边缘情况,例如指定 min-width 和 min-height),直到找到适合整个文本的区域。这将允许您拥有所需的确切宽度和高度,并且由于您可以指定确切的宽度和高度,您还可以使用 CSS 过渡来平滑扩展 div。

这是新代码:here

编辑

再摆弄一些之后,这是最终的代码。总之,我添加了什么:

  • 一个虚拟函数,您可以配置它来决定div 应该扩展多少
  • 根据TextNode的边界矩形检测div是否需要扩展
  • 平滑过渡(超时逻辑尚未使用边缘情况进行测试)

这是完整的示例:

'use strict';

const lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ac auctor augue mauris augue neque gravida in fermentum et. Accumsan in nisl nisi scelerisque. Sed pulvinar proin gravida hendrerit lectus.';

const items = Array(30).fill(0)
  .map(() => Math.ceil(lorem.length * Math.random()))
  .map(idx => lorem.substring(0, idx))
  .map(txt => `<div class="lorem-card"><div class="lorem-card--real">${txt}</div><div class="lorem-card--substitute">${txt}</div></div>`)
  .join('');

const html = `<div class="cards-container">${items}</div>`;
document.body.innerHTML += html;

function getTextWidthAndHeight() {
    return [210, 280]
}

let cards = document.querySelectorAll('.lorem-card')
let resetCardOverflow
for (let card of cards) {
		card.addEventListener('mouseover', e => {
    		clearTimeout(resetCardOverflow)
    		card.style.overflow = 'visible'
    
    		// Configure "getTextWidthAndHeight" to fit the new rectangle size needs
        let size = getTextWidthAndHeight()
        let targetReal = card.querySelector('.lorem-card--real')
        let targetSubstitute = card.querySelector('.lorem-card--substitute')
				let textNode
        for (let child of targetReal.childNodes) {
        	if (child.nodeName == '#text') {
         		textNode = child
            break
          }
        }

				// Get "height" of textNode's bounding box
        let textHeight = 0;
        let range = document.createRange();
        range.selectNodeContents(textNode);
        if (range.getBoundingClientRect) {
          var rect = range.getBoundingClientRect();
          if (rect) {
            textHeight = rect.bottom - rect.top;
          }
        }
				
        // If text exceeds box height (padding considered)
        if (textHeight > 200 - 30) {
        	targetSubstitute.style.width = `${size[0]}px`
          targetSubstitute.style.height = `${size[1]}px`
          targetSubstitute.style.transform = `translateX(-30px)`
        }
    })
    
    card.addEventListener('mouseleave', e => {
    		let targetSubstitute = card.querySelector('.lorem-card--substitute')
        targetSubstitute.style.width = ''
        targetSubstitute.style.height = ''
        targetSubstitute.style.transform = ''
        
        resetCardOverflow = setTimeout(() => {
        	card.style.overflow = ''
        }, 200)
    })
}
* {
  box-sizing: border-box;
}

.cards-container {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  align-content: space-between;
}

.lorem-card {
  position: relative;
  flex-grow: 1;
  max-width: 150px;
  max-height: 200px;
  margin: 15px;
  overflow: hidden;
}

.lorem-card--real {
  max-width: 150px;
  max-height: 200px;
  padding: 15px;
  border: 3px solid #008CBA;
  background: #FFF;
  overflow: hidden;
}

.lorem-card--substitute {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 15px;
  background: #FFF;
  border: 3px solid #008CBA;
  transition: all .2s ease;
  overflow: hidden;
}

.lorem-card:hover .lorem-card--real {
  opacity: 0;
}

.lorem-card:hover .lorem-card--substitute{
  border: 3px solid red;
  z-index: 1000;
}
<html>
  <head>
    <meta charset="utf-8">
    <title>Cards demo</title>
    <link rel="stylesheet" type="text/css" href="./cards.css">
  </head>
  <body>
  </body>
  <script src="./gen.js"></script>
</html>

如果您需要 JS 小提琴代码:here

【讨论】:

  • 这能回答问题吗?
猜你喜欢
  • 1970-01-01
  • 2017-02-24
  • 2020-08-28
  • 2022-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-21
相关资源
最近更新 更多