Vue dynamically changing background position - css

I have a list of items that are background-image and I need to go down through each item down (-38px) and display all of them changing only the background-position
enter image description here
Should be displayed in a line
enter image description here
My vue code:
CraftPage:
<template>
<div class="page-wrap">
<my-nav-craft/>
<div class="wrap">
<div class="construct">
<div class="left">
<h3 class="title">Basic</h3>
<ul class="items">
<my-li-crafts :items="items"/>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
import MyNavCraft from "#/components/navCraft/navCraft";
import MyLiCrafts from "#/components/navCraft/liCraft";
export default {
components: {MyLiCrafts, MyNavCraft},
data() {
return {
items: [
{id: 1, posX: 0, posY: 0},
{id: 2, posX: 0, posY: -38}
]
}
},
}
</script>
<style scoped>
.page-wrap{
width: 100%;
display: flex;
flex-direction: column;
max-width: 1200px;
justify-content: center;
margin: 0px auto;
}
.wrap {
width: 100%;
background-color: #c6c6c6;
border-radius: 20px;
padding: 26px 23px 8px;
}
.left {
float: left;
width: 340px;
clear: both;
margin-bottom: 5px;
}
.items{
list-style: none;
}
.items__item {
background-image: url("../assets/icon_sprite.png");
width: 36px;
height: 36px;
margin-right: 5px;
margin-bottom: 5px;
cursor: pointer;
}
</style>
My element which needs to change background-position to -38px
My-li-craft:
<template>
<li class="items__item" v-for="item in items" :key="item.id"
:style="{backgroundPosition: item.posX + 'px' + ' ' + item.posY + 'px' }"
>
</li>
</template>
<script>
export default {
name: "my-li-crafts",
props: {
items: {
type: Array,
required: true
}
},
data() {
return {
}
},
methods: {
bgPos: function () {
let el = this.$el.querySelector('.items__item'), bgWidth = 20, bgHeight = 20;
for (let i = 0; i< 3; i++) {
return el.css({
"background-position": (el.width()-bgWidth+10) + "px " + (el.height()-bgHeight-10) + "px"
})
}
}
},
// computed: {
// bgPoss: function () {
// const el = this.$el.querySelector('.items__item'), bgWidth = 20, bgHeight = 20;
// return el.css({
// "background-position": (el.width()-bgWidth+10) + "px " + (el.height()-bgHeight-10) + "px"
// })
//
// }
// }
}
</script>
<style scoped>
.items__item {
background-image: url("../../assets/icon_sprite.png");
width: 36px;
height: 36px;
margin-right: 5px;
margin-bottom: 5px;
cursor: pointer;
}
</style>

Related

When click on sidebar menu in right side routes page display

I have sideNavbar and when click on side bar menu return menu componetns on the right side.
Below i have mention the image just look same output want.
Sandboxlink:https://m7tqt3.csb.app/
SideNavbar.js
---
import "./SideNavBar.css";
const SideNavBar = () => {
const menuItems = [
{
text: "Dashboard",
icon: "icons/grid.svg",
},
{
text: "Admin Profile",
icon: "icons/user.svg",
},
{
text: "Messages",
icon: "icons/message.svg",
},
{
text: "Analytics",
icon: "icons/pie-chart.svg",
},
{
text: "File Manager",
icon: "icons/folder.svg",
},
{
text: "Orders",
icon: "icons/shopping-cart.svg",
},
{
text: "Saved Items",
icon: "icons/heart.svg",
},
{
text: "Settings",
icon: "icons/settings.svg",
},
];
return (
<div
className={
"side-nav-container"
}
>
<div className="nav-upper">
<div className="nav-heading">
(
<div className="nav-brand">
<img src="icons/Logo.svg" alt="" srcset="" />
<h2>Showkart</h2>
</div>
)
</div>
<div className="nav-menu">
{menuItems.map(({ text, icon }) => (
<a
className={ "menu-item"}
href="#"
>
<img className="menu-item-icon" src={icon} alt="" srcset="" />
{ <p>{text}</p>}
</a>
))}
</div>
</div>
</div>
);
};
export default SideNavBar;
SideNavbar.css
/* NX = not expanded */
.side-nav-container {
background-color: var(--dark);
width: 300px;
height: 100vh;
position: relative;
color: var(--light);
transition: 0.4s;
}
.side-nav-container-NX {
width: 85px;
}
.nav-upper,
.nav-heading,
.nav-menu,
.menu-item,
.nav-footer {
/* border: 2px solid white; */
display: grid;
}
.nav-heading {
grid-template-columns: 2fr 1fr;
grid-template-rows: 1fr;
height: 75px;
}
.nav-brand {
display: flex;
color: var(--light);
}
.nav-brand img {
width: 40px;
padding: 0 10px;
}
.hamburger {
background: none;
border: none;
cursor: pointer;
margin: auto;
}
.hamburger span {
display: block;
margin-top: 5px;
background-color: var(--light);
border-radius: 15px;
height: 5px;
width: 35px;
transition: 0.4s;
}
.hamburger:hover span {
background-color: var(--primary);
}
.hamburger-in:hover span:nth-child(1) {
width: 25px;
transform: translateY(4px) rotate(-25deg);
}
.hamburger-in:hover span:nth-child(2) {
width: 40px;
}
.hamburger-in:hover span:nth-child(3) {
width: 25px;
transform: translateY(-4px) rotate(25deg);
}
/* ///////////////////// */
/* ///////////////////// */
/* ///////////////////// */
/* ///////////////////// */
.hamburger-out {
margin-left: 24px;
}
.hamburger-out:hover span:nth-child(1) {
width: 25px;
transform: translate(14px, 4px) rotate(-155deg);
}
.hamburger-out:hover span:nth-child(2) {
width: 40px;
}
.hamburger-out:hover span:nth-child(3) {
width: 25px;
transform: translate(14px, -4px) rotate(155deg);
}
.nav-menu {
grid-template-rows: repeat(7, 1fr);
margin-top: 50px;
}
.menu-item {
height: 57px;
display: flex;
color: var(--light);
text-decoration: none;
text-transform: uppercase;
margin: auto 20px;
border-radius: 10px;
}
.menu-item-NX {
margin: auto;
}
.menu-item:hover {
background-color: var(--primary);
}
.menu-item img {
width: 30px;
padding: 0 20px;
}
.nav-footer {
width: 100%;
height: 87px;
position: absolute;
bottom: 0;
grid-template-rows: 1fr;
grid-template-columns: 2fr 1fr;
}
.nav-details {
display: flex;
}
.nav-details img {
width: 50px;
padding: 0 20px;
}
.nav-footer-user-name {
font-size: 18px;
font-weight: 900;
}
.nav-footer-user-position {
margin-top: -15px;
color: var(--gray);
}
.logout-icon {
width: 30px;
margin: auto;
border-radius: 90px;
padding: 20px;
margin-left: 5px;
}
.logout-icon:hover {
background-color: var(--primary);
}
Notes: React router dom version: 5.3.1.
Output look like:
enter image description here
Routes to another components on right side
Firstly, if I undestand your question, you want a side navbar which allows you to navigate to different pages with your react app. If so:
<div className="nav-menu">
{menuItems.map(({ text, icon }) => (
<a <--- The problem is here
className={ "menu-item"}
href="#"
>
<img className="menu-item-icon" src={icon} alt="" srcset="" />
{ <p>{text}</p>}
</a>
))}
</div>
You are using anchor tag which will redirect you out of react router. You should use Link tag which allows to redirect to different pages within your react app.
const menuItems = [
{
text: "Dashboard",
icon: "icons/grid.svg",
navLink:'/dashboard'
},
{
text: "Admin Profile",
icon: "icons/user.svg",
navLink:'/admin'
},...]
<div className="nav-menu">
{menuItems.map(({ text, icon, navlink }) => (
<Link
className={ "menu-item"}
to={navlink}
>
<img className="menu-item-icon" src={icon} alt=""
srcset=""
/>
{ <p>{text}</p>}
</Link>
))}
</div>
Likewise you need to step up your App.js with BrowserRouter, Routes and Route to move between pages.

Importing bootstrap breaks Navigation bar

My project is running on vue3.
I have a Side navigation bar which consists of navigation links, it works well until I import bootstrap, which causes the icons to shift slightly and the buttons become smaller in size. I've tried the same with tailwind and the same issue occurs. The exact line that breaks it is the import of bootstraps min css file. (import "bootstrap/dist/css/bootstrap.min.css", inside of main.js).Below are screenshots and snippets of code for the navigation bar.
<script>
import NavBarLink from './NavBarLink.vue'
import {collapsed, toggleNavbar, navbarWidth} from './state'
export default{
props: {},
setup() {
return { collapsed, toggleNavbar, navbarWidth };
},
components: { NavBarLink }
}
</script>
<template>
<div class="sidebar" :style="{ width: navbarWidth }">
<span #click="toggleNavbar" style="margin-bottom: 15px;">
<i :class="`${collapsed ? 'fa-solid fa-bars': 'fa-solid fa-x'}`"></i>
</span>
<span v-if="!collapsed" class="logo">website.<span style="color: orange">club</span></span>
<span v-else class="logo">w<span style="color: orange">w</span></span>
<NavBarLink to="/about" icon="fa-solid fa-magnifying-glass">Search</NavBarLink>
<NavBarLink to="/plans" icon="fa-solid fa-tag">Plans</NavBarLink>
<NavBarLink to="/fart" icon="fa-solid fa-user">Profile</NavBarLink>
</div>
</template>
<style>
:root {
--navbar-bg-color: #0e0e0e;
--navbar-item-hover: #1d1d1d;
}
</style>
<style scoped>
.sidebar{
color: white;
background-color: var(--navbar-bg-color);
float: left;
position: fixed;
z-index: 1;
top: 0;
bottom: 0;
left: 0;
bottom: 0;
padding: 0.5rem;
transition: 0.3s ease;
display: flex;
flex-direction: column;
}
.logo {
font-size: larger;
font-family: Nexa;
padding-top:15px;
padding-bottom:15px;
}
</style>
<script>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { collapsed } from './state'
export default {
props: {
to: { type: String, required: true },
icon: { type: String, required: true }
},
setup(props) {
const route = useRoute()
const isActive = computed(() => route.path === props.to)
return { isActive, collapsed }
}
}
</script>
<template>
<router-link :to="to" class="link" :class="{ active: isActive }">
<i class="icon" :class="icon"/>
<transition name="fade">
<span v-if="!collapsed">
<slot/>
</span>
</transition>
</router-link>
</template>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.1s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.link {
display: flex;
align-items: center;
cursor: pointer;
position: relative;
font-weight: 400;
user-select: none;
margin: 0.1em 0.1em;
padding: 0.4em;
border-radius: 0.25em;
height: 1.7em;
color: white;
text-decoration: none;
}
.link:hover {
transition: .17s;
background-color: var(--navbar-item-hover);
}
.link.active {
background-color: var(--navbar-item-hover);
}
.link .icon {
flex-shrink: 0;
width: 35px;
margin-right: 10px;
}
</style>
Is there a bootstrap component I can remove to make this function?

Hiding Side Nav in Hamburger Menu

I have a sidebar, that I would like to collpase on mobile devices, and only be accessed on mobile using a hamburger menu
Using React, what is the best way for me to do so? Would this still be media queriers, or is there an alternate way that would make this type of configuration more seamless?
Please note that I only want the sidebar to collapse on mobile. I do not want to give the user the ability to collpase the sidebar when viewed on desktop.
Sidebar.Js
const sidebarNavItems = [
{
display: 'Home',
to: '/'
},
{
display: 'Blog',
to: 'Blog/'
},
{
display: 'Contact Us',
to: '/contact-us/'
},
{
display: 'Sign Up',
to: '/sign-up/'
},
{
display: 'Login',
to: '/sign-in/'
},
]
const Sidebar = () => {
const [activeIndex, setActiveIndex] = useState(0);
const [stepHeight, setStepHeight] = useState(0);
const sidebarRef = useRef();
const indicatorRef = useRef();
const location = useLocation();
useEffect(() => {
setTimeout(() => {
const sidebarItem = sidebarRef.current.querySelector('.sidebar__menu__item');
indicatorRef.current.style.height = `${sidebarItem.clientHeight}px`;
setStepHeight(sidebarItem.clientHeight);
}, 50);
}, []);
// change active index
useEffect(() => {
const curPath = window.location.pathname.split('/')[1];
const activeItem = sidebarNavItems.findIndex(item => item.section === curPath);
setActiveIndex(curPath.length === 0 ? 0 : activeItem);
}, [location]);
return (
<div className='sidebar'>
<div className="sidebar__logo">
<div><img src = {Logo} alt='Logo'className='Logo' /></div>
</div>
<div ref={sidebarRef} className="sidebar__menu">
<div
ref={indicatorRef}
className="sidebar__menu__indicator"
style={{
transform: `translateX(-50%) translateY(${activeIndex * stepHeight}px)`
}}
></div>
{
sidebarNavItems.map((item, index) => (
<Link to={item.to} key={index}>
<div className={`sidebar__menu__item ${activeIndex === index ? 'active' : ''}`}>
<div className="sidebar__menu__item__icon">
{item.icon}
</div>
<div className="sidebar__menu__item__text">
{item.display}
</div>
</div>
</Link>
))
}
</div>
</div>);
};
Here is the Sidebar.CSS
.sidebar {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 20vw;
background-color: #fff;
}
.sidebar__logo {
display: grid;
place-items: center;
margin-bottom: 5vh;
}
.sidebar__menu {
position: relative;
}
.sidebar__menu__item {
display: flex;
align-items: center;
place-content: flex-start;
padding: 1rem 3rem;
font-size: 1.25rem;
font-weight: 500;
color: #555;
transition: color 0.3s ease-in-out;
}
.sidebar__menu__item.active {
color: #fff;
}
.sidebar__menu__item__icon {
margin-right: 1rem;
}
.sidebar__menu__item__icon i {
font-size: 1.75rem;
}
.sidebar__menu__indicator {
position: absolute;
top: 0;
left: 50%;
width: calc(100% - 40px);
border-radius: 5px;
background-color: #FD954E;
z-index: -1;
transform: translateX(-50%);
transition: 0.3s ease-in-out;
}

Moving around between <nav>, <section> and <main>

I am able to divide the whole page into 4 regions and they are:-
[header] (on the very top), [nav] (right below the header), [section] (on the bottom left) and [main] (on the bottom right).
In the [nav] portion, I have a list of hyperlinked items (like “item-A”, “item-B”, etc).
The task is to:-
When “item-A” is selected, the corresponding sub-list containing “A1”, “A2”, “A3” etc should be displayed on the [section] part. Similarly, if “item-B” is selected, the sub-list “B1”, “B2” should also be displayed on the [section] part (overwriting the previous content, of course).
“B2” again is hyperlinked to the file “B.html” which, when called, should be executed on the [main] portion.
How can I do all of the above?
P.S. I can finish the above if I use [frame] and [frameset]. Unfortunately, these become obsolete in HTML5.
The widths and heights of each region can be defined inside the CSS setup.
You can use buttons instead of a hyperlink and get the same output.
Below is my solution:
const displayItemA = () => {
document.getElementById("item-A").style.display = 'grid';
document.getElementById("item-B").style.display = 'none';
}
const displayItemB = () => {
document.getElementById("item-B").style.display = 'grid';
document.getElementById("item-A").style.display = 'none';
}
const mainContent = ["A1", "A2", "A3", "B1", "B2", "B3"];
const displayMain = (q) => {
document.getElementById(q).style.display = "grid";
const toNone = mainContent.filter(e => e !== q);
for (let i = 0; i < toNone.length; i++) {
document.getElementById(toNone[i]).style.display = "none";
}
}
const displayA1 = () => displayMain("A1");
const displayA2 = () => displayMain("A2");
const displayA3 = () => displayMain("A3");
const displayB1 = () => displayMain("B1");
const displayB2 = () => displayMain("B2");
const displayB3 = () => displayMain("B3");
:root {
--main-color:red;
--dark-color:#444;
}
* {
margin:0;
padding:0;
box-sizing:border-box;
}
body {
margin: 0px;
display: grid;
place-items: center;
font-size: 20px;
}
/* repeated element */
.button-nav {
text-decoration: none;
color: var(--main-color);
background: none;
border: none;
font-size: 20px;
text-align: start;
}
.header {
height: 100px;
width: 100%;
display: grid;
place-items: center;
border-bottom: 2px solid var(--dark-color);
}
.nav {
position: relative;
height: 75px;
width: 100%;
border-bottom: 2px solid var(--dark-color);
display: flex;
align-items: center;
justify-content: center;
gap: 1em;
}
.nav h2 {
position: absolute;
left: 30px;
}
.section {
position: absolute;
left: 0px;
min-height: calc(100vh - 175px);
width: 30%;
border-right: 2px solid var(--dark-color);
padding: 30px;
gap: 1em;
}
.sub-nav {
display: grid;
gap: 1em;
}
#item-A, #item-B{ display:none }
.main {
position: absolute;
left: 30%;
min-height: calc(100vh - 175px);
width: 70%;
display: grid;
padding: 30px;
gap: 1em;
text-align: center;
}
#A1 { display: grid; }
#A2, #A3, #B1, #B2, #B3 { display: none; }
<header class="header">
<h1>Header</h1>
</header>
<nav class="nav">
<h2>Nav</h2>
<button class="button-nav" onclick="displayItemA()">item-A</button>
<button class="button-nav" onclick="displayItemB()">item-B</button>
</nav>
<div>
<section class="section">
<h3>Section</h3>
<br />
<nav class="sub-nav" id="item-A">
<button class="button-nav" onclick="displayA1()">A1</button>
<button class="button-nav" onclick="displayA2()">A2</button>
<button class="button-nav" onclick="displayA3()">A3</button>
</nav>
<nav class="sub-nav" id="item-B">
<button class="button-nav" onclick="displayB1()">B1</button>
<button class="button-nav" onclick="displayB2()">B2</button>
<button class="button-nav" onclick="displayB3()">B3</button>
</nav>
</section>
<main class="main">
<h3>Main</h3>
<div id="A1">These are the contents of A1.</div>
<div id="A2">These are the contents of A2.</div>
<div id="A3">These are the contents of A3.</div>
<div id="B1">These are the contents of B1.</div>
<div id="B2">These are the contents of B2.</div>
<div id="B3">These are the contents of B3.</div>
</main>
</div>
You can also test the code at enter link description here so you can see it on a larger viewport.
Update: I made the items in the "section" area blank by default as per the OP's request. This was done by changing the value of the display of "#item-A" to "none" in the CSS file.
Stack Blitz code:
https://stackblitz.com/edit/web-platform-uwbmw4?devtoolsheight=33&file=index.html
Because it needs a1,a2,b1,b2 HTML files and stuffs it does not work here(below)(other parts work other than iframe stuff) please try above stackblitz for testing
const itemAOptions = [
{
name: 'A1',
data: 'A1.html',
},
{
name: 'A2',
data: 'A2.html',
},
];
const itemBOptions = [
{
name: 'B1',
data: 'B1.html',
},
{
name: 'B2',
data: 'B2.html',
},
];
const dataContainer = document.querySelector('#display');
const menuContainer = document.querySelector('#menu');
const navContainer = document.querySelector('#navBar');
navContainer.addEventListener('click', (e) => {
e.stopPropagation();
if ('nochange' in e.target.dataset) {
e.preventDefault();
const toLoad = e.target.dataset.nochange;
let data = '';
if (toLoad === 'loadA') {
data = constructData(itemAOptions);
} else {
data = constructData(itemBOptions);
}
menuContainer.innerHTML = data;
display.innerHTML = ``;//emptydata means clear or empty screen as you requested
}
});
menuContainer.addEventListener('click', (e) => {
e.stopPropagation();
e.preventDefault();
const toLoad = e.target;
let data = '';
data = `<iframe src="https://web-platform-uwbmw4.stackblitz.io/${toLoad.innerHTML}.html" name="targetframe" allowTransparency="true" scrolling="no" frameborder="0" >
</iframe>`;//use src="Your.Website.address.or.directory/${toLoad.innerHTML}.html"
console.log(toLoad.innerHTML);
display.innerHTML = data;
});
function constructData(item) {
let innerData = '';
item.forEach((i) => {
innerData += `<li class="li">${i.name}</li>`;
});
return `<ul class="ul">${innerData}</ul>`;
}
function constructDataIframe(item) {
let innerData = '';
item.forEach((i) => {
innerData += `<iframe src="https://web-platform-uwbmw4.stackblitz.io/${i.data}" name="targetframe" allowTransparency="true" scrolling="no" frameborder="0" >
</iframe>`;
});
return `${innerData}`;
}
h1 {
text-align:center;
}
#navBar{
background-color: transparent;
border: 5px solid black;
text-align: center;
}
.lk{
text-decoration: none;
}
.uli{
display: flex;
justify-content: flex-end;
}
.lik{
list-style: none;
padding-right:15px;
}
.a{
text-decoration: none;
}
.li{
list-style: none;
}
.ul{
float:left;
padding-left: 10px;
}
#secHolder{
width: 100%;
background-color: transparent;
border: 5px solid black;
text-align: center;
}
#display{
width: 100%;
background-color: transparent;
border: 5px solid black;
text-align: center;
}
#Holder{
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
}
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<h1>Header</h1>
<nav id="navBar">
<ul>
<div class="uli">
<li class="lik">Item A</li>
<li class="lik">Item B</li>
</div>
</ul>
</nav>
<div id="Holder">
<div id="secHolder">
<h3>Section</h3>
<section id="menu"></section>
</div>
<main id="display"></main>
</div>
<script src="script.js"></script>
</body>
</html>
P.S I have tried my best on answering but I don't have regular practice on commenting please try to understand, ask for doubts.
I cannot understand what you mean by I cannot use frame its obsolete in html5, I have used iframe is it ok?
I didn't concentrate on stylings. It is recommended to use iframe instead of object tag. You can try exploring that as well.
const itemAOptions = [{
name:"A1",
data:"A1.html"
}, {
name:"A2",
data:"A2.html"
}];
const itemBOptions = [{
name:"B1",
data:"B1.html"
}, {
name:"B2",
data:"B2.html"
}];
const dataContainer = document.querySelector("#display");
const menuContainer = document.querySelector("#menu");
const navContainer = document.querySelector("#navBar");
navContainer.addEventListener("click", (e)=>{
e.stopPropagation();
if("nochange" in e.target.dataset){
e.preventDefault();
const toLoad = e.target.dataset.nochange;
let data = '';
if(toLoad === "loadA"){
data = constructData(itemAOptions);
}
else{
data = constructData(itemBOptions);
}
menuContainer.innerHTML = data;
}
});
menuContainer.addEventListener("click", (e)=>{
e.stopPropagation();
if("spaload" in e.target.dataset){
e.preventDefault();
const data = e.target.dataset.spaload;
dataContainer.innerHTML=`<object type="text/html" data=${data} ></object>`;
}
});
function constructData(item){
let innerData = '';
item.forEach((i)=>{
innerData+=`<li><a href=${i.data} data-spaload=${i.data}>${i.name}</a></li>`;
});
return `<ul>${innerData}</ul>`;
}
<nav id="navBar">
<ul>
<li>Item a</li>
<li>Item B</li>
</ul>
</nav>
<h3>Section</h3>
<section id="menu"></section>
<main id="display"></main>
const embed = document.getElementById("main-frame");
const navLinksContainer = document.getElementById("nav-links");
const subLinksContainer = document.getElementById("sub-links");
const pageLinks = {
"item-A": {
A1: "https://en.wikipedia.org/wiki/Australia",
A2: "https://en.wikipedia.org/wiki/Austria",
A3: "https://en.wikipedia.org/wiki/America",
},
"item-B": {
B1: "https://en.wikipedia.org/wiki/Barbados",
B2: "https://en.wikipedia.org/wiki/Bahamas",
B3: "https://en.wikipedia.org/wiki/Brazil",
},
"item-C": {
C1: "https://en.wikipedia.org/wiki/Canada",
C2: "https://en.wikipedia.org/wiki/Cayman_Islands",
C3: "https://en.wikipedia.org/wiki/Chile",
},
};
// Creates the page link element
function createPageLink(text, href = "") {
const listItem = document.createElement("LI");
const anchor = document.createElement("A");
anchor.innerHTML = text;
anchor.href = href;
listItem.appendChild(anchor);
listItem.anchor = anchor;
return listItem;
}
window.onload = function loadHandler() {
// build all the sub links for each nav link.
Object.keys(pageLinks).forEach((text) => {
const subSectionLinks = Object.keys(pageLinks[text]).map((subText) => {
const subLink = createPageLink(subText, pageLinks[text][subText]);
subLink.anchor.addEventListener("click", function (event) {
event.preventDefault();
embed.src = event.target.href;
});
return subLink;
});
// replaces the sub links in the left section
// for each corresponding nav link that's clicked.
const navLink = createPageLink(text);
navLink.anchor.addEventListener("click", function (event) {
event.preventDefault();
subLinksContainer.innerHTML = "";
subSectionLinks.forEach((element) =>
subLinksContainer.appendChild(element)
);
embed.src = "";
});
navLinksContainer.appendChild(navLink);
});
};
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul {
list-style-type: none;
}
a {
text-decoration: none;
color: red;
font-size: 28px;
}
a:hover {
color: #920000;
}
body {
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
font-size: 32px;
color: #222;
}
.page {
width: 702px;
height: 918px;
margin: 50px auto;
display: grid;
gap: 0;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(10, 1fr);
grid-template-areas:
"h h h h"
"h h h h"
"n n n n"
"s m m m"
"s m m m"
"s m m m"
"s m m m"
"s m m m"
"s m m m"
"s m m m";
}
.page > * {
border: 1px solid #777;
}
.page > header {
grid-area: h;
display: flex;
justify-content: center;
align-items: center;
}
.page > nav {
grid-area: n;
display: flex;
flex-direction: row;
align-items: center;
}
.page > section {
grid-area: s;
text-align: center;
padding-top: 15px;
}
.page > main {
grid-area: m;
text-align: center;
}
.page > nav div {
width: 25%;
text-align: center;
}
.page > nav ul {
width: calc(100% - 25%);
text-align: center;
}
.page > nav ul li {
display: inline;
}
.page > nav ul li:nth-of-type(n + 2) {
margin-left: 15px;
}
.page > section ul {
margin-top: 50px;
text-align: left;
text-indent: 40px;
}
.page > section ul li {
margin-bottom: 10px;
}
.page > main p {
height: 8%;
margin-top: 20px;
}
.page > main iframe {
width: 100%;
height: calc(92% - 20px);
border: none;
overflow-x: hidden;
}
<div class="page">
<header>Header</header>
<nav>
<div>nav</div>
<ul id="nav-links"></ul>
</nav>
<section>
section
<ul id="sub-links"></ul>
</section>
<main>
<p>main</p>
<iframe allowTransparency="true" id="main-frame"></iframe>
</main>
</div>
Update:
I added a line of code to clear the iframe whenever the user selects a different link from the navigation bar as per the comments made by the OP.

I have a VueJs 3 menu component and need to set cursor for selected element on render

I'm so far really pleased with how simple it was to setup a menu that has a sliding cursor to show where you are hovering:
<template>
<div class="menu">
<div class="logo">
<span>**Logo Here **</span>
</div>
<div class="menuBar" ref="menuBar">
<ul>
<li v-for='topLevel in menu' :key='topLevel.id' :ref='topLevel.id' v-on:mouseover="moveCursor(topLevel.id)" >
<span><a :href="/topLevel.link/">{{ topLevel.text }}</a></span>
<ul>
<li v-for='child in topLevel.children' :key='child.text'>
<span><a :href="/child.link/">{{ child.text }}</a></span>
</li>
</ul>
</li>
</ul>
<div class="cursor" :style="{ left: cursorPosition, width: cursorWidth, visibility: cursorVisible }" ></div>
</div>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'Header',
setup() {
const cursorPosition = ref('0px');
const cursorWidth = ref('0px');
const cursorVisible = ref('visible');
const menu = ref([
{
text: 'Device Access',
id: 'device_access',
children: [
{
text: 'Interactive',
link: '/connection_center'
},{
text: 'Reservation',
link: '/reserve_probe'
}, {
text: 'Reservation Vue',
link: '/reservation.html'
}
]
}, {
text: 'Automation',
id: 'automation',
children: [
{
text: 'Builder',
link: '/builder',
},{
text: 'Execution Results',
link: '/test_suite_execution_results'
},
]
}, {
text: 'Site Admin',
id: 'site_admin',
children: [
{
text: 'Accounts',
link: '/admin_accounts',
},{
text: 'Settings',
link: '/admin_settings',
}
]
},{
text: 'Reporting',
id: 'reporting',
children: [
{
text: 'Administrative',
link: '/administrative_reporting'
},{
text: 'Historical',
link: '/historical_reporting'
},
]
}, {
text: 'Help',
id: 'help',
children: [
{
text: 'User Documentation',
link: '/docs/'
},{
text: 'API Documentation',
link: '/apidocsindex'
}
]
}
]);
return {
menu,
cursorPosition,
cursorWidth,
cursorVisible
}
},
methods: {
mouseover: function(refId) {
this.moveCursor(refId);
},
selectCurrent: function() {
this.moveCursor(this.selectedMenuRef);
},
moveCursor: function(refId) {
var menuRect = this.$refs.menuBar.getBoundingClientRect();
var refItem = this.$refs[refId];
var width = refItem.offsetWidth;
var itemRect = refItem.getBoundingClientRect();
this.cursorPosition = (itemRect.left - menuRect.left) + "px";
this.cursorWidth = width + "px";
}
}
// {
// link: '/Session/logout', text: 'Logout',
// [
// { link: '#' event='preferences', text: 'Preferences',
// ]
// }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
#import "../styles/_colors.scss";
div.menu {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 65px;
background: $headerBackground;
border-bottom: 3px solid $headerBorder;
> .logo {
height: 72px;
padding-left: 10px;
padding-top: 3px;
float: left;
}
> .menuBar {
float: left;
height: calc(100% - 20px);
width: 900px;
margin: 15px 0 0 50px;
float: left;
position: relative;
> .cursor {
position: absolute;
height: 3px;
background: $menuCursor;
transition: left 0.2s ease-in-out, width 0.3s ease-in-out;
}
ul {
list-style-type: none;
display: block;
padding: 0;
margin: 0;
}
> ul {
width: 100%;
display: block;
> li {
float: left;
display: block;
text-indent: none;
position: relative;
margin: 0;
padding: 12px 25px 18px 15px;
&::after {
position: absolute;
top: 21px;
right: 10px;
width: 4px;
height: 4px;
border-bottom: 1px solid #000000;
border-right: 1px solid #000000;
content: "";
transform: rotate(45deg);
transition: border-color 0.2s ease;
}
&:hover {
> span > a {
color: $menuHover;
}
> ul {
height: auto;
transition: all;
transition-duration: 0.1s;
border: 1px solid $menuBorder;
border-top: none;
}
}
> span {
text-align: left;
font-size: 14px;
line-height: 16px;
> a {
text-decoration: none;
color: $menuText;
text-transform: uppercase;
}
}
> ul {
height: 0;
position: absolute;
top: 50px;
left: 0;
width: 175px;
background: $headerBackground;
border: none;
overflow: hidden;
margin: 0;
padding: 0;
> li {
margin: 0;
display: block;
padding: 0px 5px 10px 20px;
> span {
text-align: left;
font-size: 14px;
line-height: 16px;
> a {
text-decoration: none;
color: $menuText;
}
}
&:hover > span > a {
color: $menuHover;
}
}
}
}
}
}
}
</style>
I want to run the method selectCurrent after the menu is rendered to have the cursor hover over the current selected menu option, but everything I've tried so far blows up in undefined refs.
Call the method in the onMounted hook in setup().
onMounted(() => {
console.log('mounted!');
call the selectCurrent method here
})
Don't forget to import it:
import { onMounted, ref} from 'vue'

Resources