gpt4 book ai didi

javascript - 如何实现类似于 youtube 卡片的上下文菜单?

转载 作者:行者123 更新时间:2023-12-01 15:12:06 24 4
gpt4 key购买 nike

我正在学习一点 CSS,今天我发现自己想要实现一个类似的上下文菜单(当你点击 youtube 卡片上的垂直省略号时显示的那个)但我的尝试没有成功很远:D。这是我所拥有的:

<!DOCTYPE html>
<html>

<head>
<style type="text/css">
.container {
display: inline-block;
position: relative;
}

.dropdown {
position: absolute;
right: 1rem;
top: 1rem;
}

.dropdown-opener {
cursor: pointer;
user-select: none;
position: absolute;
right: 0;
}

.dropdown .dropdown-toggle,
.dropdown .dropdown-menu {
display: none;
style-type: none;
}

.dropdown .dropdown-toggle:checked+ul {
display: block;
}

.style-scope .menu-renderer {
--layout-inline_-_display: inline-flex;
--icon-button-icon-height: 24px;
--icon-button-icon-width: 24px;
--spec-icon-active-other: #606060;
--spec-icon-inactive: #909090;
--spec-text-disabled: #909090;
--spec-text-secondary: #606060;
align-items: var(--layout-center-center_-_align-items);
color: var(--menu-renderer-button-color, var(--spec-icon-inactive));
cursor: pointer;
display: var(--layout-inline_-_display);
fill: var(--iron-icon-fill-color, currentcolor);
width: var(--icon-button-icon-width, 100%);
background: transparent;
}
</style>
</head>

<body>
<div class="container">
<img alt="sample" src="https://via.placeholder.com/200x200">
<nav class="dropdown layer--topright">
<label class="dropdown-opener" for="menu-opener1">...</label>
<input class="dropdown-toggle" id="menu-opener1" type="checkbox">
<ul class="dropdown-menu">
<li>Foo1</li>
<li>Bar1</li>
<li>Baz1</li>
</ul>
</nav>
</div>
<div class="container">
<img alt="sample" src="https://via.placeholder.com/200x200">
<nav class="dropdown layer--topright">
<label class="dropdown-opener" for="menu-opener1">...</label>
<input class="dropdown-toggle" id="menu-opener1" type="checkbox">
<ul class="dropdown-menu">
<li>Foo2</li>
<li>Bar2</li>
<li>Baz2</li>
</ul>
</nav>
</div>
<div class="container">
<img alt="sample" src="https://via.placeholder.com/200x200">
<nav class="dropdown layer--topright">
<icon-button id="button" class="dropdown-opener dropdown-trigger style-scope menu-renderer">
<button id="button" class="style-scope icon-button" aria-label="Action menu">
<icon class="style-scope menu-renderer">
<svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope icon" style="pointer-events: none; display: block; width: 100%; height: 100%;">
<g class="style-scope icon">
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" class="style-scope icon"></path>
</g>
</svg>
</icon>
</button>
</icon-button>
<input class="dropdown-toggle" id="menu-opener2" type="checkbox">
<ul class="dropdown-menu">
<li>Foo3</li>
<li>Bar3</li>
<li>Baz3</li>
</ul>
</nav>
</div>
</body>

</html>


如您所见,代码很脏而且损坏了...但是我的尝试存在重大问题,我不知道如何解决:
  • 单击省略号按钮时出现错误的上下文菜单
  • 显示元素符号的元素
  • 与真正干净的 youtube 相比,元素风格很难看

  • 因此,如果您能解释如何解决这些问题,以便从我的尝试中获得可用的东西,那就太棒了。

    或者,如果我的方法全错了,无法使用并准备扔进垃圾箱……您能否解释一下实现我的目标的最佳方法是什么?

    提前致谢。

    最佳答案

    问题 #1
    单击溢出菜单按钮时出现错位的上下文菜单。上下文菜单实际上并没有放错位置。 for 中的属性<label> element 指的是错误的上下文菜单。例如,for您的 <label> 的值(value)第一个 .container 中的元素值为 menu-opener1 ,但 for <label> 上的值 中的元素第二 .container具有完全相同的值。单击任一标签会导致第一个容器中的下拉菜单打开,因为这两个标签都会导致选中第一个容器上的隐藏复选框。
    我们能做什么?只需更改 id值,以便每个下拉菜单都有一个唯一的 id 值。然后,将该 id 值用于 for <label> 中的值元素。

    问题 #2
    隐藏li的公告ul 中的元素, 你必须使用 list-style-type: none 在您的 CSS 和 上不是 style-type: none .

    问题 #3
    这是一个非常主观的问题。一个设计对一个人来说可能看起来很干净,但对其他人来说却是不干净的。尽管如此,我还是试图实现我想看到的。您可以更改以下一些内容以改进设计方面。

  • 更改您的字体系列,使其适合您的主题。我在这里选择了一个无衬线字体系列。
  • 为您的上下文菜单添加背景。在这里,我选择了白色。
  • 在每个 li 之间添加空格元素。在这里,我使用了line-height .您也可以使用paddingmargin在每个 li元素。
  • 添加 box-shadow显示海拔。 Google's Material Design recommends using this technique to show that an element's z-position is higher.
  • 当上下文菜单从可见变为不可见时允许转换,反之亦然。这意味着避免使用不可转换的 CSS 属性(例如 visibilitydisplay )。在这里,我选择使用transform: scaleopacity过渡。

  • 其他问题
    从语义上讲,您的 HTML 标记不正确。
  • <nav> (navigation) 元素用于在页面之间导航。在这里,您可能应该使用 <menu> element instead .但是,由于它仍处于试验阶段,我选择使用 <section> .
  • 您正在使用一些不存在的 HTML 标签(例如 <icon-button><icon> )。尝试咨询here for valid HTML tagshere for valid SVG tags .
  • 最后.container item 有一个按钮,可以检查隐藏的复选框。但是,the <button> element does not work with <label> element .因此,尝试制作 <label>元素在视觉上看起来像一个按钮。您可以使用:active:hover CSS 伪选择器分别在按下和悬停时更改按钮样式。此外,这减少了嵌套。
  • 当可以使用 CSS 进行样式设置时,尽量避免使用内联样式(例如,您的内联 SVG 样式)
  • 个人喜好。大多数框架使用 .container包含整个页面,所以我选择使用类名 .box而不是 .container .

  • 这是可运行的片段。

    * {
    font-family: Helvetica;
    box-sizing: border-box;
    }

    .box {
    display: inline-block;
    position: relative;
    }

    .dropdown {
    position: absolute;
    right: 0;
    top: 8px;
    }

    .dropdown-opener {
    cursor: pointer;
    user-select: none;
    position: absolute;
    width: 24px;
    height: 24px;
    background: url('https://i.imgur.com/Qt3Qwgp.png');
    background-repeat: no-repeat;
    background-position: right;
    right: 0;
    }

    .dropdown .dropdown-toggle {
    display: none;
    }

    .dropdown .dropdown-menu {
    list-style-type: none;
    transform: scale(0);
    opacity: 0;
    transition:
    transform 0.25s ease,
    opacity 0.25s ease;

    position: absolute;
    top: 1.5em;
    right: 10px;
    line-height: 1.75em;
    background: white;
    box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.5);
    border-radius: 5px;
    padding: 20px;
    margin: 0;
    transform-origin: top right;
    }

    .dropdown .dropdown-toggle:checked + ul {
    transform: scale(1);
    opacity: 1;
    }

    .dropdown-opener-button {
    position: absolute;
    width: 24px;
    height: 24px;
    right: 8px;
    top: 0;
    }

    .icon-button {
    padding: 0;
    border: 0;
    border-radius: 5px;
    cursor: pointer;
    transition:
    box-shadow .25s ease,
    background .25s ease,
    transform .25s ease;
    background: #ffffffdd;
    }

    .icon-button:hover {
    box-shadow: 0px 0px 2px 0px rgba(0,0,0,0.35);
    }

    .icon-button:active {
    background: #ffffff77;
    transform: scale(0.9);
    }

    .icon-button ~ .dropdown-menu {
    top: 1.75em;
    }
    <div class="box">
    <img alt="sample" src="https://via.placeholder.com/200x200">
    <section class="dropdown layer--topright">
    <label class="dropdown-opener" for="menu-opener1"></label>
    <input class="dropdown-toggle" id="menu-opener1" type="checkbox">
    <ul class="dropdown-menu">
    <li>Foo1</li>
    <li>Bar1</li>
    <li>Baz1</li>
    </ul>
    </section>
    </div>
    <div class="box">
    <img alt="sample" src="https://via.placeholder.com/200x200">
    <section class="dropdown layer--topright">
    <label class="dropdown-opener" for="menu-opener2"></label>
    <input class="dropdown-toggle" id="menu-opener2" type="checkbox">
    <ul class="dropdown-menu">
    <li>Foo2</li>
    <li>Bar2</li>
    <li>Baz2</li>
    </ul>
    </section>
    </div>
    <div class="box">
    <img alt="sample" src="https://via.placeholder.com/200x200">
    <section class="dropdown layer--topright">
    <label class="dropdown-opener-button icon-button" for="menu-opener3">
    <svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope icon">
    <g class="style-scope icon">
    <path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" class="style-scope icon"></path>
    </g>
    </svg>
    </label>
    <input class="dropdown-toggle" id="menu-opener3" type="checkbox">
    <ul class="dropdown-menu">
    <li>Foo3</li>
    <li>Bar3</li>
    <li>Baz3</li>
    </ul>
    </section>
    </div>


    更新
    由于没有清楚地理解 OP 的含义,即当用鼠标悬停时使菜单项改变状态有多难,我决定创建悬停效果和单击时的不同效果(波纹) <li>元素。我推荐阅读这篇 writing on creating a ripple effect .
    此外,根据要求,添加了在菜单框外单击时隐藏菜单的功能。这是可运行的片段。

    // Closing menu on outside click
    const outsideClickListener = event => {
    let checkedToggle = document.querySelector('.dropdown-toggle:checked')
    let openedMenu = document.querySelector('.dropdown-toggle:checked + .dropdown-menu')

    // If click is performed on checkbox (through label), do nothing
    if (event.target.classList.contains('dropdown-toggle')) {
    return
    }

    // If click is performed on label, uncheck all other dropdown-toggle
    if (event.target.classList.contains('dropdown-opener') ||
    event.target.classList.contains('dropdown-opener-button')) {
    let forId = event.target.getAttribute('for')
    document.querySelectorAll('.dropdown-toggle').forEach(toggle => {
    if (forId !== toggle.getAttribute('id'))
    toggle.checked = false
    })
    return
    }

    // If click is performed outside opened menu
    if (openedMenu && !openedMenu.contains(event.target)) {
    checkedToggle.checked = false
    }
    }
    document.addEventListener('click', outsideClickListener)

    // Ripple effect on li elements
    const createRipple = event => {
    let li = event.target
    let liBox = li.getBoundingClientRect()
    let x = event.pageX - liBox.left
    let y = event.pageY - liBox.top
    let animDuration = 350
    let animationStart, animationFrame

    let animationStep = timestamp => {
    if (!animationStart) animationStart = timestamp
    let frame = timestamp - animationStart
    if (frame < animDuration) {
    let easing = (frame / animDuration) * (2 - (frame / animDuration))
    let circle = `circle at ${x}px ${y}px`
    let color = `rgba(0, 0, 0, ${0.2 * (1 - easing)})`
    let stop = `${100 * easing}%`
    li.style.backgroundImage = `radial-gradient(${circle}, ${color} ${stop}, transparent ${stop})`
    animationFrame = window.requestAnimationFrame(animationStep)
    }
    else {
    li.style.backgroundImage = ''
    window.cancelAnimationFrame(animationStep)
    }
    }

    animationFrame = window.requestAnimationFrame(animationStep)
    }
    const listItems = document.querySelectorAll('li')
    listItems.forEach(li => {
    li.addEventListener('click', createRipple)
    })
    * {
    font-family: Helvetica;
    box-sizing: border-box;
    }

    .box {
    display: inline-block;
    position: relative;
    }

    .dropdown {
    position: absolute;
    right: 0;
    top: 8px;
    }

    .dropdown-opener {
    cursor: pointer;
    user-select: none;
    position: absolute;
    width: 24px;
    height: 24px;
    background: url('https://i.imgur.com/Qt3Qwgp.png');
    background-repeat: no-repeat;
    background-position: right;
    right: 0;
    }

    .dropdown .dropdown-toggle {
    display: none;
    }

    .dropdown .dropdown-menu {
    list-style-type: none;
    transform: scale(0);
    opacity: 0;
    transition:
    transform 0.25s ease,
    opacity 0.25s ease;

    position: absolute;
    top: 1.5em;
    right: 10px;
    background: white;
    box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.5);
    border-radius: 5px;
    margin: 0;
    transform-origin: top right;
    padding: 7.5px 0 7.5px 0;
    }

    .dropdown .dropdown-toggle:checked + ul {
    transform: scale(1);
    opacity: 1;
    }

    .dropdown-menu li {
    padding: 7.5px;
    padding-left: 25px;
    cursor: pointer;
    transition: background .15s ease;
    }

    .dropdown-menu li:hover {
    background: #00000012;
    }

    .dropdown-opener-button {
    position: absolute;
    width: 24px;
    height: 24px;
    right: 8px;
    top: 0;
    }

    .dropdown-opener-button svg {
    pointer-events: none;
    }

    .icon-button {
    padding: 0;
    border: 0;
    border-radius: 5px;
    cursor: pointer;
    transition:
    box-shadow .25s ease,
    background .25s ease,
    transform .25s ease;
    background: #ffffffdd;
    }

    .icon-button:hover {
    box-shadow: 0px 0px 2px 0px rgba(0,0,0,0.35);
    }

    .icon-button:active {
    background: #ffffffaa;
    box-shadow: 0px 0px 4px 0px rgba(0,0,0,0.35);
    transform: scale(0.9);
    }

    .icon-button ~ .dropdown-menu {
    top: 1.75em;
    }
    <div class="box">
    <img alt="sample" src="https://via.placeholder.com/200x200">
    <section class="dropdown layer--topright">
    <label class="dropdown-opener" for="menu-opener1"></label>
    <input class="dropdown-toggle" id="menu-opener1" type="checkbox">
    <ul class="dropdown-menu">
    <li>Foo1</li>
    <li>Bar1</li>
    <li>Baz1</li>
    </ul>
    </section>
    </div>
    <div class="box">
    <img alt="sample" src="https://via.placeholder.com/200x200">
    <section class="dropdown layer--topright">
    <label class="dropdown-opener" for="menu-opener2"></label>
    <input class="dropdown-toggle" id="menu-opener2" type="checkbox">
    <ul class="dropdown-menu">
    <li>Foo2</li>
    <li>Bar2</li>
    <li>Baz2</li>
    </ul>
    </section>
    </div>
    <div class="box">
    <img alt="sample" src="https://via.placeholder.com/200x200">
    <section class="dropdown layer--topright">
    <label class="dropdown-opener-button icon-button" for="menu-opener3">
    <svg viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" focusable="false" class="style-scope icon">
    <g class="style-scope icon">
    <path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" class="style-scope icon"></path>
    </g>
    </svg>
    </label>
    <input class="dropdown-toggle" id="menu-opener3" type="checkbox">
    <ul class="dropdown-menu">
    <li>Foo3</li>
    <li>Bar3</li>
    <li>Baz3</li>
    </ul>
    </section>
    </div>

    关于javascript - 如何实现类似于 youtube 卡片的上下文菜单?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62366173/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com