I am creating a tabbed navigation bar wherein when the tab is active it should change its color the color that i set it to change with. Navigating thru the pages with the tabs works fine however the color highlight on the active tab just don't seem to work.
here is my code so far:
HTML:
<section class="tab" id="active_Current_Tabs">
<header>Active/Current Tabs</header>
<nav class="navbar">
<ul>
<li class="btn currentTab" onclick="activeTab(event, 'lorem7')">TAB1</li>
<li class="btn currentTab" onclick="activeTab(event, 'lorem8')">TAB2</li>
<li class="btn currentTab" onclick="activeTab(event, 'lorem9')">TAB3</li>
</ul>
</nav>
<main class="main-doc">
<article class="selectedPage" id='lorem7'>
<h2>Lorem</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, est?</p>
</article>
<article class="selectedPage" id="lorem8">
<h2>Lorem</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, est?</p>
</article>
<article class="selectedPage" id="lorem9">
<h2>Lorem</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, est?</p>
</article>
</main>
</section>
CSS:
article {
text-align: center;
width: 100%;
height: 300px;
max-height: 300px;
margin: 0;
}
/*navbar css*/
nav {
width: 100%;
height: 75px;
padding: 0;
margin: 0;
}
nav ul {
height: 100%;
padding: 0;
display: flex;
list-style: none;
justify-content: space-around;
}
.btn {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
width: 100%;
height: 100%;
background-color: #8b9d98;
cursor: pointer;
font-weight: 500;
}
.btn:hover {
background-color: #d7e0e0;
font-weight: 700;
transition: .5s;
}
/*main css*/
main {
margin-top: 0;
}
/*Active/Current Tab */
#lorem7 {
display: flex;
flex-direction: column;
background-color: #49c2a4;
align-items: center;
justify-content: center;
}
#lorem8 {
display: none;
flex-direction: column;
background-color:#35386f;
align-items: center;
justify-content: center;
}
#lorem9 {
display: none;
flex-direction: column;
background-color:#e28968;
align-items: center;
justify-content: center;
}
Javascript:
// active/current tab function
function activeTab(evnt, currPage) {
var currenttab;
var pages = document.getElementsByClassName('selectedPage');
for (i = 0; i < pages.length; i++) {
pages[i].style.display = "none";
}
//for dehighlighting inactive tabs
currenttab = document.getElementsByClassName('currentTab');
for(j = 0; j < currenttab.length; j++) {
currenttab[j].className = currenttab[j].className.replace("green", " ");
}
document.getElementById(currPage).style.display = "flex";
evnt.currentTarget.className += "green"; //this appends the color to active tab
}
help please! T_T
I'm not sure what you were trying to do with the "green" class because there were no rules for it in your CSS. I answered the question assuming you wanted the active tab to be the same color as the active page. Sorry if that's not what you intended, but I think it makes sense.
To avoid problems with specific class names, you can use .classList methods like "add" and "remove". This way you don't have to worry about the order of class names in the markup. Examples:
tabs[i].classList.remove('active')
e.currentTarget.classList.add('active')
You can also attach your event listener (click handler) dynamically to keep your HTML uncluttered. Example:
for(j = 0; j < tabs.length; j++) {
// attach event listener to all tabs
tabs[j].addEventListener('click', clickTab)
}
You can also make your CSS less repetitive by assigning similar styles to a common class:
.page {display:none;}
.page.active {
display:flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
I modified your IDs in order to be able to reference tabs and pages independently without explicitly passing parameters to the click handler functions. Example:
<li id="t2" class="tab">TAB2</li>
...
<article class="page" id="p2">...</article>
Here is my JS Bin:
http://jsbin.com/defidih/edit?html,css,js,console,output
evnt.currentTarget.className += "green";
This line will add to the already existing className.
So your class class="btn currentTab"
becomes class="btn currentTabgreen"
instead of class="btn currentTab green" if you didn't add green to it before.
So it would better to use currenttab[j].className.replace("green", ""); to reset the previous green classes and evnt.currentTarget.className += " green"; to set the new green class.
Edit: this does imply that the classname will keep growing with one space every time. SO the optimal would be to use classList.add() and classList.remove() instead of manually editting the class string.
function activeTab(evnt, currPage) {
var currenttab;
var pages = document.getElementsByClassName('selectedPage');
for (i = 0; i < pages.length; i++) {
pages[i].style.display = "none";
}
//for dehighlighting inactive tabs
currenttab = document.getElementsByClassName('currentTab');
for(j = 0; j < currenttab.length; j++) {
currenttab[j].className = currenttab[j].className.replace("green", "");
}
document.getElementById(currPage).style.display = "flex";
evnt.currentTarget.className += " green"; //this appends the color to active tab
}
article {
text-align: center;
width: 100%;
height: 300px;
max-height: 300px;
margin: 0;
}
/*navbar css*/
nav {
width: 100%;
height: 75px;
padding: 0;
margin: 0;
}
nav ul {
height: 100%;
padding: 0;
display: flex;
list-style: none;
justify-content: space-around;
}
.btn {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
width: 100%;
height: 100%;
background-color: #8b9d98;
cursor: pointer;
font-weight: 500;
}
.btn:hover {
background-color: #d7e0e0;
font-weight: 700;
transition: .5s;
}
/*main css*/
main {
margin-top: 0;
}
/*Active/Current Tab */
#lorem7 {
display: flex;
flex-direction: column;
background-color: #49c2a4;
align-items: center;
justify-content: center;
}
#lorem8 {
display: none;
flex-direction: column;
background-color:#35386f;
align-items: center;
justify-content: center;
}
#lorem9 {
display: none;
flex-direction: column;
background-color:#e28968;
align-items: center;
justify-content: center;
}
.green {
background-color: green;
}
<section class="tab" id="active_Current_Tabs">
<header>Active/Current Tabs</header>
<nav class="navbar">
<ul>
<li class="btn currentTab" onclick="activeTab(event, 'lorem7')">TAB1</li>
<li class="btn currentTab" onclick="activeTab(event, 'lorem8')">TAB2</li>
<li class="btn currentTab" onclick="activeTab(event, 'lorem9')">TAB3</li>
</ul>
</nav>
<main class="main-doc">
<article class="selectedPage" id='lorem7'>
<h2>Lorem</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, est?</p>
</article>
<article class="selectedPage" id="lorem8">
<h2>Lorem</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, est?</p>
</article>
<article class="selectedPage" id="lorem9">
<h2>Lorem</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus, est?</p>
</article>
</main>
</section>
To create a tabbed navigation bar wherein when the tab is active it should change its color to a custom color that you have set. You could use this few lines of vanilla JavaScript.
JS:
var activeTab;
var acctOptions = document
.querySelector(".account-options")
.querySelectorAll("li");
acctOptions.forEach(option => {
option.addEventListener("click", function() {
if (activeTab) activeTab.classList.remove("active");
activeTab = option;
activeTab.classList.add("active");
});
});
CSS:
.active {
background: blue;
}
HTML:
<ul class="account-options">
<li class='login'><a>Login</a></li>
<li class='register'>Register</li>
<li class='account' ><a>My Account</a></li>
<li class='reward-points'><a>Reward Points</a></li>
<li class='password-reset'><a>Reset Password</a></li>
<li class='logout'><a>Logout</a></li>
</ul>
Related
I'm not sure why my code is not responding, everything looks correct to me. I'm new to using jQuery and trying to use toggleClass to have card info displayed when the card on my page is clicked. Initially, I'd like it to be hidden but able to be toggled on and off.
$(document).ready(function() {
$('.card').click(function() {
$(this).toggleClass('inner-card.active');
});
});
.card {
display: flex;
flex-direction: column;
justify-content: end;
width: 350px;
height: 180px;
background: lightgreen;
border: 2px solid black;
margin-left: 8px;
margin-bottom: 8px;
cursor: pointer;
}
.inner-card .active {
display: none;
}
.inner-card {
display: flex;
flex-direction: column;
justify-content: end;
background: rgba(255, 165, 0, 0.5)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="card">
<div class="inner-card">
<h5>Title</h5>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sunt, libero?</p>
Link goes here
<div class="img-div">
<img src="../static/img/search.png" class="card-img" alt="">
</div>
</div>
</div>
It should go from this
To this
I believe there are several problems. First of all, I believe you want to write the display:none CSS logic this way:
.inner-card.active{
display: none;
}
This means that the inner card will be hidden if it also has the active class.
Secondly, I believe you need to rewrite the script this way:
$(document).ready( function(){
$('.card').click( function() {
$(this).find(".inner-card").toggleClass('active');
});
});
When you use the toggleClass you need to use just the name of the class, not the selector (-> no dot). Also, from the CSS, it looks like you need to find the inner card element first.
Fiddle link
You probably want something like this:
$(document).ready(function() {
$('.card').click(function() {
$(this).find(".inner-card").toggleClass('hidden');
});
});
.card {
display: flex;
flex-direction: column;
justify-content: end;
width: 350px;
height: 180px;
background: lightgreen;
border: 2px solid black;
margin-left: 8px;
margin-bottom: 8px;
cursor: pointer;
}
.hidden {
display: none !important;
}
.inner-card {
display: flex;
flex-direction: column;
justify-content: end;
background: rgba(255, 165, 0, 0.5)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="card">
<div class="inner-card hidden">
<h5>Title</h5>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sunt, libero?</p>
Link goes here
<div class="img-div">
<img src="../static/img/search.png" class="card-img" alt="magnify lens">
</div>
</div>
</div>
i tried to make an element appear after a user clicks on an image. i used classlist.toggle for that but when i click on the image the error message "Uncaught TypeError: Cannot read property 'toggle' of undefined" appears in the console. And of course it is not working 🙁
const plusbtn = document.querySelector(".btn-img");
const drop = document.getElementsByClassName("boxes")
plusbtn.addEventListener('click', () => {
drop.classList.toggle("box-drop");
})
.boxes {
border: 1px solid rgba(39, 81, 197, 0.39);
margin-top: 50px;
padding: 15px;
display: none;
flex-direction: row;
justify-content: space-around;
}
.btn-img {
align-self: center;
filter: grayscale(50%);
cursor: pointer;
}
.box-drop {
display: flex;
}
<section>
<h1>RESOURCES</h1>
<div class="resource-boxes">
<div class="box1">
<div class="top">
<img src="../images/plus.png" alt="" class="btn-img">
</div>
<div class="boxes">
<a href="">
<div class="minibox">
<span class="minibox-txt">Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet consectetur, adipisicing elit. Numquam, enim! </span>
</div>
</a>
</div>
</div>
</div>
</section>
getElementsByClassName returns a HTMLCollection. You need to loop every element in that collection in order to access it.
But, since you already use .querySelector() you could as well
use .querySelectorAll() which returns a NodeList which offers by default a .forEach() Method
Instead of hiding elements where they are styles-defined, use instead a reusable utility class like .u-none{ display: none; }
const EL_btnPlus = document.querySelector(".btn-img");
const ELS_drop = document.querySelectorAll(".boxes");
EL_btnPlus.addEventListener('click', () => {
ELS_drop.forEach(EL => EL.classList.toggle("u-none"));
});
.boxes {
display: flex;
}
/* Utility classes */
.u-none {
display: none;
}
<img class="btn-img" src="https://lh3.googleusercontent.com/a/AATXAJzyljorAsMuzZ3OQ6B7g_2EU0bkA4lk3iH076AR=k-s64" alt="">
<div class="boxes u-none">Box one</div>
<div class="boxes u-none">Box two</div>
drop is a list of elements (see the return value of getElementsByClassName). You have to loop through it and toggle the class on each individual element:
const plusbtn = document.querySelector(".btn-img");
const drop = [...document.getElementsByClassName("boxes")];
plusbtn.addEventListener('click', () => {
drop.forEach(e => e.classList.toggle("box-drop"));
})
.boxes {
border: 1px solid rgba(39, 81, 197, 0.39);
margin-top: 50px;
padding: 15px;
display: none;
flex-direction: row;
justify-content: space-around;
}
.btn-img {
align-self: center;
filter: grayscale(50%);
cursor: pointer;
}
.box-drop {
display: flex;
}
<section>
<h1>RESOURCES</h1>
<div class="resource-boxes">
<div class="box1">
<div class="top">
<img src="https://www.gravatar.com/avatar/0fdacb141bca7fa57c392b5f03872176?s=48&d=identicon&r=PG&f=1" alt="" class="btn-img">
</div>
<div class="boxes">
<a href="">
<div class="minibox">
<span class="minibox-txt">Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet consectetur, adipisicing elit. Numquam, enim! </span>
</div>
</a>
</div>
</div>
</div>
</section>
I have 2 top bars - Primary and Secondary Nav Bar as shown in below image. When Scrolled Down the secondary bar becomes sticky and the primary hides.
When I scroll down, the secondary nav bar needs to be sticky and primary should hide.
Now when I scroll up, both primary and secondary needs to appear.
Codepen/Source Code Link
Note: with smooth transitions so that it doesn't like jump
Something like shown below
React JS Code
const {createRef, useState, useEffect} = React
function Header({ children, sticky=false, className }){
const [isSticky, setIsSticky] = useState(false)
const ref = React.createRef()
// mount
useEffect(()=>{
const cachedRef = ref.current,
observer = new IntersectionObserver(
([e]) => setIsSticky(e.intersectionRatio < 1),
{threshold: [1]}
)
observer.observe(cachedRef)
// unmount
return function(){
observer.unobserve(cachedRef)
}
}, [])
return (
<header className={isSticky ? " isSticky" : ""} ref={ref}>
{children}
</header>
)
}
function App(){
return <React.Fragment>
<section>
<h1>Primary Header</h1>
</section>
<Header>
<ul class="menu-items">
<li>C Language</li>
<li>C++ Language</li>
<li>Java</li>
<li>C# Language</li>
<li>Python</li>
<li>PHP</li>
<li>Javasript</li>
<li>R Language</li>
</ul>
</Header>
<div class="content" id="container">
<h3>On Scroll Sticky Header</h3>
<p>The header will stick to the top when you reach its scroll position.</p>
<p>Scroll back up to remove the sticky effect.</p>
<p>Some text to enable scrolling.. Lorem ipsum dolor sit amet, illum definitiones no quo, maluisset concludaturque
<div>
</React.Fragment>
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
CSS
// spacer
section{
margin:0;
height: 100px;
background-color: #f1f1f1;
display: flex;
align-items: center;
justify-content: center;
}
// smart sticky header
header{
position: sticky;
top: -1px;
padding: 4px 10px;
background: #555;
color: #f1f1f1;
transition: .2s ease-out;
text-align: center;
}
.content {
padding: 16px;
}
.secondary-header {
padding: 10px 16px;
background: #555;
color: #f1f1f1;
width: 100%;
}
.menu-items {
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap;
list-style-type: none;
> li {
padding: 0 15px;
}
}
Using Javascript, I have multiple element and when I hover that element, it changes the content and element.
I tried using mouseover and mouseout, it's working for single element, but doesn't work when I have multiple element.
I add loops for the parent, but still not working, also now when I hover the first element, it's just looping over.
What am I doing wrong?
const wrapper = document.querySelectorAll(".wrapper");
wrapper.forEach(function () {
let mouseWhite = document.querySelector(".commercial-white");
let mouseBlue = document.querySelector(".commercial-blue");
mouseWhite.addEventListener("mouseover", function (e) {
mouseBlue.classList.add("open");
e.stopPropagation();
})
mouseBlue.addEventListener("mouseout", function (e) {
mouseBlue.classList.remove("open");
e.stopPropagation();
})
})
.wrapper {
display: flex;
position: relative;
}
.commercial-white {
background-color: #f1f1f4;
width: 240px;
height: 260px;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-bottom: 15px solid #005da0;
margin: 10px;
}
.commercial-blue {
position: absolute;
background-color: #005da0;
color: #FFFFFF;
width: 240px;
height: 273px;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 10px;
border: 2px solid #FFFFFF;
visibility: hidden;
}
.open.commercial-blue {
visibility: visible;
}
.commercial-flip {
padding: 20px;
}
<div class="wrapper">
<div class="commercial-white">
<div class="commercial-flip">
<img style="width:100px" src="http://g-ec2.images-amazon.com/images/G/31/img14/anywhere/amazon-logo-500500._V327001990_.jpg">
<h1>Your title</h1>
</div>
</div>
<div class="commercial-blue">
<div class="commercial-flip">
<img style="width:100px" src="https://images-na.ssl-images-amazon.com/images/G/01/gc/designs/livepreview/amazon_dkblue_noto_email_v2016_us-main._CB468775337_.png">
<h1>Lorem Ipsum</h1>
<p>lorem ipsum dolor si amet lorem ipsum dolor si amet lorem ipsum dolor si amet</p>
</div>
</div>
</div>
<div class="wrapper">
<div class="commercial-white">
<div class="commercial-flip">
<img style="width:100px" src="http://g-ec2.images-amazon.com/images/G/31/img14/anywhere/amazon-logo-500500._V327001990_.jpg">
<h1>Your title</h1>
</div>
</div>
<div class="commercial-blue">
<div class="commercial-flip">
<img style="width:100px" src="https://images-na.ssl-images-amazon.com/images/G/01/gc/designs/livepreview/amazon_dkblue_noto_email_v2016_us-main._CB468775337_.png">
<h1>Lorem Ipsum</h1>
<p>lorem ipsum dolor si amet lorem ipsum dolor si amet lorem ipsum dolor si amet</p>
</div>
</div>
</div>
You need to 'querySelect' on each wrapper, not the document because in your code document.querySelector(".commercial-white") will only give you the first occurence of .commercial-white .. and it will do that twice, but it will still be the same occurence... the first one in the document (twice).
Your querySelectorAll gave you a NodeList of elements wrapper.
forEach will go through each element of your NodeList (kind of) like an array. wrapper[0] wrapper[1]...
The argument aWrapper is just the name for "The current element being processed in the NodeList".
This way, aWrapper.querySelector will only "select" within the current element wrapper in the loop.
wrapper.forEach(function (aWrapper) {
let mouseWhite = aWrapper.querySelector(".commercial-white");
let mouseBlue = aWrapper.querySelector(".commercial-blue");
const wrapper = document.querySelectorAll(".wrapper");
wrapper.forEach(function(aWrapper) {
let mouseWhite = aWrapper.querySelector(".commercial-white");
let mouseBlue = aWrapper.querySelector(".commercial-blue");
mouseWhite.addEventListener("mouseenter", function(e) {
console.log("e.target.classList mouseWhite :", e.target.classList);
mouseBlue.classList.toggle("open");
})
mouseBlue.addEventListener("mouseout", function(e) {
console.log("e.target.classList mouseBlue :", e.target.classList);
mouseBlue.classList.toggle("open");
})
})
.wrapper {
display: flex;
position: relative;
}
.commercial-white {
background-color: #f1f1f4;
width: 240px;
height: 260px;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-bottom: 15px solid #005da0;
margin: 10px;
}
.commercial-blue {
position: absolute;
background-color: #005da0;
color: #FFFFFF;
width: 240px;
height: 273px;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 10px;
border: 2px solid #FFFFFF;
visibility: hidden;
}
.open.commercial-blue {
visibility: visible;
}
.commercial-flip {
padding: 20px;
pointer-events: none;
}
<div class="wrapper">
<div class="commercial-white">
<div class="commercial-flip">
<img style="width:100px" src="http://g-ec2.images-amazon.com/images/G/31/img14/anywhere/amazon-logo-500500._V327001990_.jpg">
<h1>Your title</h1>
</div>
</div>
<div class="commercial-blue">
<div class="commercial-flip">
<img style="width:100px" src="https://images-na.ssl-images-amazon.com/images/G/01/gc/designs/livepreview/amazon_dkblue_noto_email_v2016_us-main._CB468775337_.png">
<h1>Lorem Ipsum</h1>
<p>lorem ipsum dolor si amet lorem ipsum dolor si amet lorem ipsum dolor si amet</p>
</div>
</div>
</div>
<div class="wrapper">
<div class="commercial-white">
<div class="commercial-flip">
<img style="width:100px" src="http://g-ec2.images-amazon.com/images/G/31/img14/anywhere/amazon-logo-500500._V327001990_.jpg">
<h1>Your title</h1>
</div>
</div>
<div class="commercial-blue">
<div class="commercial-flip">
<img style="width:100px" src="https://images-na.ssl-images-amazon.com/images/G/01/gc/designs/livepreview/amazon_dkblue_noto_email_v2016_us-main._CB468775337_.png">
<h1>Lorem Ipsum</h1>
<p>lorem ipsum dolor si amet lorem ipsum dolor si amet lorem ipsum dolor si amet</p>
</div>
</div>
</div>
Edit :
For the buggy display I just added "pointer-events: none;" to .commercial-flip and it was enough :
.commercial-flip {
padding: 20px;
pointer-events: none; }
I am fairly new to JS (so I may be wrong), but if I understand correctly, it work because if .commercial-flip can receive pointer events then when it's hovered, it's parent is not.
Before this modification, as long as you didn't enter the border of .commercial-flip it worked just fine.
So apparently hovering a child isn't the same as hovering it's parent if the child can catch the event, but if it can't it's all good.
Edit : Preventing Child from firing parent's click event
Note : I used classList.toggle instead of classList.add and classList.remove, and mousenter instead of mouseover but it's just a personal preference here ; it gives the exact same result.
You have to query the childs:
wrapper.forEach(function (el) {
let mouseWhite = el.querySelector(".commercial-white");
let mouseBlue = el.querySelector(".commercial-blue");
....
You're looping over each one but using the same selector which causes some issues. Just change your loop to the following so there's a reference to each element:
wrapper.forEach(function (el) {
let mouseWhite = el.querySelector(".commercial-white");
let mouseBlue = el.querySelector(".commercial-blue");
mouseWhite.addEventListener("mouseover", function (e) {
mouseBlue.classList.add("open");
e.stopPropagation();
})
mouseBlue.addEventListener("mouseout", function (e) {
mouseBlue.classList.remove("open");
e.stopPropagation();
})
})
<div class="b-wrapper d-flex d-flex-center">
<div class="inner-wrapper">
<h2 class="b-animate h b-from-top b-delay03">Project 3</h2>
<p class="b-animate p b-from-right b-delay03">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</div>
When trying to add the styles of position: relative; right: -100%; to the h2 element the text moves right 100%. However, if I change the styles to position: relative; top: -100%; the text refuses to move up 100%. I'm assuming this has someting to do with flexbox and positioning. What should I do to fix this?
.d-flex-center {
justify-content: center;
align-items: center;
}
.d-flex {
display: flex!important;
}
.b-wrapper .inner-wrapper {
margin: 0 15px -17px 15px;
}
Top positioning is not working here, because you are using a percentage value, while the container has no height. It would be the same for a positive value too.
To see your top/bottom positioning take effect, either
Give the containing element a height, so the percentage value will be calculated based on that
or
Use a pixel value for top/bottom positioning
Using % value
Parent container must have specified height, so that percentage will be calculated correctly.
.d-flex-center {
justify-content: center;
align-items: center;
}
.d-flex {
display: flex!important;
}
.b-wrapper .inner-wrapper {
margin: 0 15px -17px 15px;
}
.inner-wrapper {
height: 150px; /* container height */
}
h2 {
position: relative;
right: -40%;
top: -20%; /* -20% is now equivalent to -30px (20% of 150px height) */
}
<div class="b-wrapper d-flex d-flex-center">
<div class="inner-wrapper">
<h2 class="b-animate h b-from-top b-delay03">Project 3</h2>
<p class="b-animate p b-from-right b-delay03">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</div>
Using px value
Pixel value will always be consistent, as it is not calculated based on another value. (No need to specify height on parent container.)
.d-flex-center {
justify-content: center;
align-items: center;
}
.d-flex {
display: flex!important;
}
.b-wrapper .inner-wrapper {
margin: 0 15px -17px 15px;
}
h2 {
position: relative;
right: -40%;
top: -30px; /* -30px === -30px */
}
<div class="b-wrapper d-flex d-flex-center">
<div class="inner-wrapper">
<h2 class="b-animate h b-from-top b-delay03">Project 3</h2>
<p class="b-animate p b-from-right b-delay03">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</div>