Skip to main content

[JS30] Day26: Stripe Follow Along Dropdown

這一天的主題是要實作和stripe官網類似的 navbar,他會隨著滑鼠移到不同的連結,跟隨出現對應的內容,且對應的內容之中的連結都是可以點擊的。

HTML

.top的 nav 包住全部,內層為.dropdownBackground的背景白框和.cool的 unordered list,裡面有三個 list(hover point),而每個 list 裡面又在包了各自的連結(ul.dropdown>li)

<h2>Cool</h2>
//
用來最後的測試,這是為什麼我們要抓到dropdown的上左方推移後,還要再減去nav的上左方推移
<nav class="top">
<div class="dropdownBackground">
<span class="arrow"></span>
</div>

<ul class="cool">
<li>
<a href="#">About Me</a>
<div class="dropdown dropdown1">
<div class="bio">
<img src="https://logo.clearbit.com/wesbos.com" />
<p>
Wes Bos sure does love web development. He teaches things like
JavaScript, CSS and BBQ. Wait. BBQ isn't part of web development. It
should be though!
</p>
</div>
</div>
</li>
<li>
<a href="#">Courses</a>
<ul class="dropdown courses">
<li>
<span class="code">RFB</span>
<a href="https://ReactForBeginners.com">React For Beginners</a>
</li>
<li>
<span class="code">ES6</span>
<a href="https://ES6.io">ES6 For Everyone</a>
</li>
<li>
<span class="code">NODE</span>
<a href="https://LearnNode.com">Learn Node</a>
</li>
<li>
<span class="code">STPU</span>
<a href="https://SublimeTextBook.com">Sublime Text Power User</a>
</li>
<li>
<span class="code">WTF</span>
<a href="http://Flexbox.io">What The Flexbox?!</a>
</li>
<li>
<span class="code">GRID</span>
<a href="https://CSSGrid.io">CSS Grid</a>
</li>
<li>
<span class="code">LRX</span>
<a href="http://LearnRedux.com">Learn Redux</a>
</li>
<li>
<span class="code">CLPU</span>
<a href="http://CommandLinePowerUser.com">Command Line Power User</a>
</li>
<li>
<span class="code">MMD</span>
<a href="http://MasteringMarkdown.com">Mastering Markdown</a>
</li>
</ul>
</li>
<li>
<a href="#">Other Links</a>
<ul class="dropdown dropdown3">
<li><a class="button" href="http://twitter.com/wesbos">Twitter</a></li>
<li>
<a class="button" href="http://facebook.com/wesbos.developer"
>Facebook</a
>
</li>
<li><a class="button" href="http://wesbos.com">Blog</a></li>
<li>
<a class="button" href="http://wesbos.com/courses">Course Catalog</a>
</li>
</ul>
</li>
</ul>
</nav>

CSS

hover point,也就是前述提到的 li,設為相對位置,.dropdown設為絕對位置

.cool > li {
position: relative;
display: flex;
justify-content: center;
}

.dropdown {
opacity: 0;
position: absolute;
overflow: hidden;
padding: 20px;
top: -20px;
border-radius: 2px;
transition: all 0.5s;
transform: translateY(100px);
will-change: opacity;
display: none;
}

若是hover point,滑鼠移動到上面會新增.trigger-enter,過一段時間(js 設定為 150ms),會新增.trigger-enter-active,背景會新增.open

.trigger-enter .dropdown {
display: block;
}

.trigger-enter-active .dropdown {
opacity: 1;
}
.dropdownBackground.open {
opacity: 1;
}

JS

透過在 triggers 的mouseentermouseleave事件,來新增 class。而我們也要同時設定白色背景的位置,透過getBoundingClient(),可以得到.dropdown元素的寬高、上左方推移,而要再減去整個 nav 的上左方推移,最後在設定白色背景的widthheighttransform屬性。

const triggers = document.querySelectorAll(".cool > li");
const background = document.querySelector(".dropdownBackground"); // white background
const nav = document.querySelector(".top");

function handleEnter() {
this.classList.add("trigger-enter");
setTimeout(
() =>
this.classList.contains("trigger-enter") &&
this.classList.add("trigger-enter-active"),
150
);
background.classList.add("open");

const dropdown = this.querySelector(".dropdown"); // hover到的li底下的dropdown
const dropdownCoords = dropdown.getBoundingClientRect();
const navCoords = nav.getBoundingClientRect();

const coords = {
height: dropdownCoords.height,
width: dropdownCoords.width,
top: dropdownCoords.top - navCoords.top,
left: dropdownCoords.left - navCoords.left,
};

background.style.setProperty("width", `${coords.width}px`);
background.style.setProperty("height", `${coords.height}px`);
background.style.setProperty(
"transform",
`translate(${coords.left}px, ${coords.top}px)`
);
}

function handleLeave() {
this.classList.remove("trigger-enter", "trigger-enter-active");
background.classList.remove("open");
}

triggers.forEach((trigger) =>
trigger.addEventListener("mouseenter", handleEnter)
);
triggers.forEach((trigger) =>
trigger.addEventListener("mouseleave", handleLeave)
);