sub-menu expansion and innerHTML replacement based on onclick, using just Javascript - javascript

First, I should mention that I'm about 4-weeks new to the coding world, and this is the first time I'm trying to make (what I thought would be) a simple site.
I have seen many similar questions on Stack Overflow, but in trying to adapt the code samples provided in the solutions, the solution would stop working.
So, the current hurdle is:
I have a menu defined in HTML with a sub-menu in one of the <li> elements ("Portfolio"), and that <li> element contains the character ▼ (&#9660).
I set up an onclick event for that <li> element so that when it was clicked it would do two things: expand/display the sub-menu <li> elements directly below it (pushing the other <li> elements in the menu further down), and replace the ▼ character with a ▲ character (&#9650)... until the <li> element was clicked again to shrink/hide the sub-menu.
I'm not sure if it matters, but this menu is inside a grid item because the page is set up using CSS Grid.
So basically:
HOME
ABOUT US
PORTFOLIO ▼
INFORMATION
CONTACT
...would become:
HOME
ABOUT US
PORTFOLIO ▲
LINK 1
LINK 2
LINK 3
INFORMATION
CONTACT
No matter how I set up my classes and IDs, I cannot get the arrow symbol to swap, and somewhere along the line, I messed up the coding and now the sub-menu doesn't even expand anymore.
It's likely embarrassingly bad code (given that I've tried to mash together bits from samples I've seen) but here is what I have. Thanks in advance.
var arrowstring = document.getElementById("arrowdirection").innerHTML;
document.getElementById("IDforPortfolioLink").classList.toggle("show");
if (IDforPortfolioLink.classList.contains('show')) {
arrowstring = "&#9650"
} else {
arrowstring = "&#9660"
}
arrowdirection.textContent = arrowstring;
}
.sub-menu-content {
display: none;
position: absolute;
}
.sub-menu-content a {
display: block;
}
.sub-menu-content a:hover {
background-color: green;
}
.show {
display: block;
}
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<ul class="menu">
<li>Home</li>
<li>About us</li>
<li class="LinkForPortfolio" id="IDforPortfolioLink" onclick= "myFunction()">LINK <span class="arrow" id= "arrowdirection">▼</span><div class="sub-menu-content" id="myportfolio">
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
</div>
</li>
<li>Information</li>
<li>Contact</li>
</ul>
</body>
</html>

Well, your code had so many flaws I had to rewrite it.
I'll explain everything that I possibly can of what I did here:
I changed the HTML a bit: I have added div's, instead of ul's with li's, inside a nav(container). It's more indicated to do so because it keeps the markup clean, and is less harder to debug.
I have assigned nav a display: flex; justify-content: center; align-items: center; which centers the divs inside nav, and inlines them. I did so with nav div, which pretty much centered the text inside of them.
I have removed all of the classes expect of .portfolio because it's useless to have that many classes.
I made div.expand-portfolio a child of div.portfolio, which in itself(.expand-portfolio) has another ul child, which holds the links. You might've noticed that I've added .portfolio a position: relative; and .expand-portfolio a position: absolute;. I did that because, I wanted to take .expand-portfolio out of the document flow, which basically means I wanted to make .expand-portfolio not interact with any element on the page. Now, when assigning position: absolute; to a child inside a container, the child's position is going to be relative to the document and not the parent. This is why you may add position: relative; to the parent.
I created a separate class called .expanded which gives .expand-portfolio a height of 150px when assigned to it.
You also might have noticed I gave the divs inside the nav a transition: 500ms ease, what that does is make the transition between the properties smooth, and not sudden. You may remove that property from them if you don't want that.
Now, the javascript.
When I made those 3 variables, which are the references of the elements from the page, you noticed I used document.getElementsByClassName followed by a [0]. What document.getElementsByClassName() returns is: a nodelist. Documentation here. It's basically a sort of "array", and with [0] appended to it, I select only the first and only element of the page with that class.
You may have observed I added the onclick function in the javascript file. Personal preference. I said that when I click the portfolio button, first, you should change that span's innerHTML. (the span element holds the actual symbol). I also said you should toggle the .expanded class. And, I made an if statement, checking if .expand-portfolio doesn't contain the class. If it doesn't, you can pretty much see what it does.
I hope it helps. If you have any more questions, ask them in the comments.
var portfolio = document.getElementsByClassName("portfolio")[0];
var portfolioInner = document.getElementsByClassName("inner-html")[0];
var expandPortfolio = document.getElementsByClassName("expand-portfolio")[0];
portfolio.onclick = function(){
portfolioInner.innerHTML = "▲";
expandPortfolio.classList.toggle("expanded");
if(!expandPortfolio.classList.contains("expanded")){
portfolioInner.innerHTML = "▼"
}
};
html, body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
.menus-container {
height: 100%;
width: auto;
}
.menus-container > div {
padding: 10px 10px 5px;
transition: 500ms ease;
width: 30%;
height: 100%;
cursor: pointer;
}
.expand-portfolio {
overflow: hidden;
width: 100px;
height: 0;
background-color: #000;
transition: 500ms ease;
}
.expand-portfolio ul {
padding-left: 25px;
}
.expand-portfolio ul li {
padding: 10px 0 10px 0;
color: #fff;
}
.portfolio span {
margin-left: 5px;
}
.expanded {
height: 150px;
}
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="menus-container">
<div>Home</div>
<div>About Us</div>
<div class="portfolio">Portfolio <span class="inner-html">&#9660</span>
<div class="expand-portfolio">
<ul>
<li>LINK 1</li>
<li>LINK 2</li>
<li>LINK 3</li>
</ul>
</div>
</div>
<div>Information</div>
<div>Contact</div>
</div>
</body>
</html>

Related

How to make active button in navigation bar change color

Ok so i'm super beginner with html and css and i don't know javascript at all.I'm creating a little website as a school project, i made horizontal navigation bar from w3schools tutorial, what i want to do is when i press one of the buttons to stay colored, not just change color for 1 sec because they are 'active'. My code may be completely messy but i really need help.
Also i have 3 more subpages connected to this one, i want them to stay colored as well.
What i'm trying to achieve is exactly this: How can I add class on active li with JavaScript code
But it doesnt work for me, maybe i need to change something in javascrip because my class is named 'navbar'?
I've tried several solves from this topic on stack overflow but none of these work for me :\
HTML:
<ul class="navbar">
<li>Pocetna</li>
<li>Stranica 2</li>
<li>Stranica 3</li>
<li style="float: right;">Kontakt</li>
</ul>
CSS:
.navbar {
list-style-type: none;
position: sticky;
top: 0;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
.navbar li {
float: left;
}
.navbar li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-family: Arial;
}
.navbar li a:hover {
background-color: #111;
}
Im expecting link to stay orange when im on that page.
you can do some things with jquery like add an event listener that changes the css of html elements
const changeColor = () => {
$('ul > li > a').css('background-color', 'inherit')
$(event.target).css("background-color", "red")
}
$('ul > li > a').on('click', changeColor)
https://jsfiddle.net/z02ndowt/
You can do this by adding a class onto your html <a> tag on the link that is active and then just style the active class within your CSS. See below:
HTML
<ul class="navbar">
<li><a class="active" href="sajt.html">Pocetna</a></li>
<li>Stranica 2</li>
<li>Stranica 3</li>
<li style="float: right;">Kontakt</li>
</ul>
CSS
.active {
color: orange;
}
Ok so i did some testing and kinda found a solution. I put identificator on instead of class. So on my main page i put id="active" on first link, on my second page on second link etc. then just added #active { background-color: orange; } and it works just how i wanted it to work.

On button click show information sliding up and pushing the button upwards using jQuery

I have a foreach loop which displays a list of items using relative and absolute positioning, and on the bottom I would like to add a button (which is at the bottom of the container), which when pressed, shows/hides the given information, pushing the button with itself. I've looked at a couple of stackoverflow questions which had basically the same problem, but I couldn't find a solution which would work in my case.
Here are the codes for the problem (since I've tried a couple solutions, the style positions might not be logical, if you see anything weird please let me know):
The view:
<ul class="events>
#foreach (var events in Model)
{
//absolute positioned div-s
<li>
<div class="eventActions">
<button class="toggleBet">Place bet</button>
#Html.ActionLink("Event details", "Details", "Event", new { eventId = events.Id }, null)
<div class="betContent">#Html.Partial("_BetPartial", new BetViewModel(events))</div>
</div>
</li>
</ul>
The styles:
.events > li .eventActions {
position: absolute;
bottom: 0;
right: 0;
font-size: 24px;
height: 200px;
}
.events > li .toggleBet {
display: inline-block;
}
.events > li .betContent {
background-color: green;
margin: 0;
max-height: 0;
overflow: hidden;
transition: max-height 1s;
}
.events > li .eventActions.open .betContent {
max-height: 300px;
}
The jQuery:
$(".toggleBet").on("click",function(e) {
$(this.parentNode).toggleClass("open");
});
Here is a fiddle which shows what I would like to achieve: http://jsfiddle.net/yeyene/fpPJz/3/ (credits to user yeyene, from this question)
And here is the picture of my project so far (I would like to extend the list items height, move the links lower and make them move up when clicked)
Thank you in advance!
I would suggest forgetting about the .slideToggle method and just using a CSS class on the parent container, then use the max-height property to toggle between open and closed (or just height if you already know exactly how big the container should be when opened).
Here's a simple fiddle showing how you can do this with "pure" CSS by just adding a class to a container: https://jsfiddle.net/8ea3drce/
For good measure, below is the code used in the above JS fiddle:
HTML
<div class="container">
<a class="trigger">Trigger</a>
<ol class="content">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ol>
</div>
CSS
.container {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.container .trigger {
display: inline-block;
background-color: red;
color: white;
cursor: pointer;
padding: 1em;
}
.container .content {
background-color: lightblue;
margin: 0;
max-height: 0; // This suppresses the element's height.
overflow: hidden; // This keeps internal elements from being visible when height is suppressed.
transition: max-height .5s; // This animates the motion when max-height is released. This isn't usually perfect. The closer max-height comes to be with the actual height of the element, the better. Fixed heights might be ideal.
}
.container.open .content {
max-height: 300px; // This releases the element's height to be as large as it would naturally be, up to 500px.
}
Javascript/jQuery
$('.trigger').on('click', function(e) {
$(this.parentNode).toggleClass('open');
})
Using the idea of classtoggling as shown in Dom's answer, setting the absolute position's anchors correctly and deleting the interfering height attribute solved the problem!

Predict element width with JavaScript

I want to render tab elements on the page. The tabs are inline-blocks, so if there are too many tabs on a single row, the remaining tabs will automatically wrap themselves into a new row. I do not want this. What i want is that if there are too many tabs, an arrow icon is rendered at the end of the tab elements, which makes it possible to cycle between tab groups.
The problem is that the width of the container element which contains all the tabs can change dynamically, the number of tabs can change and also the width of every tab is different.
Would it be possible to make a JavaScript function which tells me that if these tabs were rendered on the container element then how many of them would fit on a single row? Even the width of all the rendered tabs would be useful.
I could use this function to calculate the number of tabs I can render on the page and put the remaining tabs in a different group.
Looks like I can insert the tabs in the container, get their sizes and remove them from the DOM before the next render event happens. This way I can calcuate the sizes of the to-be-rendered tabs to put them in groups.
Here is a brief example how it can be done:
https://jsfiddle.net/fqkx9krs/
HTML
<!-- Normally the tab list is generated with JSX, but in this simple example we just grab it from here-->
<ul class="tabs-list hidden" id="tabs">
<li>Tab 1</li>
<li>This is Tab 2</li>
<li>And this Tab 3</li>
</ul>
<br>
<br>
<br>
<div class="tabs" id="container"></div>
CSS
.tabs {
width: 200px;
background-color: grey;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
li {
display: inline-block;
background-color: blue;
padding: 3px;
color: white;
border: 1px solid black;
}
.hidden {
visibility: hidden;
}
JS
function predictElementSizes() {
var tabs = document.getElementById("tabs");
var container = document.getElementById("container");
container.appendChild(tabs);
var lis = tabs.childNodes;
lis.forEach(elem => {
if (elem.getBoundingClientRect) {
console.log(elem.getBoundingClientRect().width);
}
});
tabs.parentNode.removeChild(tabs);
}
setTimeout(predictElementSizes, 1000);
Maybe this is what you are looking for
carouFredSel plugin
https://github.com/Codeinwp/carouFredSel-jQuery
demo
http://www.colemuseum.org/js/

Span and list elements wrap in DIV

I have tried to combine a span element and a list that uses in-block formatting. There seems to be so misalignment of the elements.
Also, would anyone know how to wrap the elements within a fixed width?
Her's a link
<http://jsfiddle.net/joewaldronrit/3nhdnbL8/#&togetherjs=97QmIzvPKD>?
CSS:
.word-sugg-hint{
position:absolute;
top: 50px;
text-align: left;
font-size: 12px;
padding-right: 0px;
color:rgb(32,106,138);
}
.sugg-details{
display:inline-block;
text-align:left;
}
ul.suggestion-list li{
display: inline-block;
font-size: 14px;
height:0px;
}
ul.suggestion-list{
display: inline-block;
font-size: 12px;
margin-left: 0px;
padding-bottom:3px;
}
ul.suggestion-list li:hover{
color:rgb(105, 131, 73);
cursor:pointer;
text-decoration: underline;
}
ul.suggs.suggestion-list li{
/*
width:180px;
overflow:hidden;
text-overflow: ellipsis;
white-space: nowrap;
*/
float: left;
height: 20px;
color:#0000FF;
font-size:14px;
display:inline-block;
padding:0px;
}
Javascript:
var crateItems = ["apples", "bananas", "grapefruit"];
var suggList = document.getElementById("suggestion-list");
for (var i = 0; i < suggList.children.length; i++) {
if (crateItems.length === i) break;
suggList.children[i].innerHTML = crateItems[i] + (i < crateItems.length - 1 ? "," : "");
}
HTML
<div class="word-sugg-hint" id ="sugg-div">
<h class="sugg-details"> Did you mean? </h>
<ul id="suggestion-list" class="suggestion-list suggs">
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
<li>
</li>
</ul>
</div>
This answer assumes that instead of an <h> element, you mean <span>.
The main reason your elements were misaligned was your float:left was clashing with your display: inline-block. That solved your first problem. Your second question, how to wrap elements within a fixed width. If you see my jsFiddle, I simply wrapped the span and ul with a div that had a class called 'wrap'. I gave that element a fixed width. I also gave it a background color so you can easily see the width of each div. Because div's are block elements, I had to make it display as inline-block. You will see the text is aligned, and the widths are the same. Take away the background color, and I think it renders the way you wish: http://jsfiddle.net/3nhdnbL8/2/
As a greater take away, may I suggest, when working with CSS to get the desired look, too often we keep adding stuff. It is important to remember that when you add something, that may clash with something that already exists. Each time you want to add something, I would first ask if there is anything you should take out. It is a good idea to plan our CSS the same way we plan our JavaScript. Too much CSS causes a great deal of conflicts, and can become very difficult to debug.
Good luck.

Highlight active tab in navigation menu [duplicate]

This question already has an answer here:
How to highlight active tab on the website menu?
(1 answer)
Closed 9 years ago.
My question is :
I have a menu items, and I want to highlight the active tab that users switch to that points to another page for sure .
stackover flow use :
.nav {
float: left;
font-size: 125%;
}
.nav ul {
margin: 0;
}
.nav li {
background: none repeat scroll 0 0 #777777;
display: block;
float: left;
margin-right: 7px;
}
**.nav .youarehere {
background: none repeat scroll 0 0 #FF9900;
}**
.youarehere a {
color: #FFFFFF;
}
.nav li:hover {
background-color: #FF9900;
}
.nav a {
color: #FFFFFF;
display: block;
padding: 6px 12px;
text-decoration: none;
}
Can anybody tell me what else they use to make this work ?
menu :
<ul class="nav">
<li> <a href="{$smarty.const._URL}/index.{$smarty.const._FEXT}" class="wide-nav-link menu_link" >{$lang.homepage}</a></li>
<li class="dropdown">
{$lang.category} <b class="caret"></b>
<ul class="dropdown-menu menu_link">
{dropdown_menu_video_categories}
</ul>
</li>
{if $smarty.const._MOD_ARTICLE == 1}
<li class="dropdown">
{$lang.articles} <b class="caret"></b>
<ul class="dropdown-menu menu_link">
{dropdown_menu_article_categories}
</ul>
</li>
{/if}
<li> {$lang.top_videos}</li>
<li>{$lang.new_videos}</li>
<li>{$lang.random_video}</li>
{if isset($mm_menu_always_inject1)}{$mm_menu_always_inject1}{/if}
<li>{$lang.contact_us}</li>
{if isset($mm_menu_always_inject2)}{$mm_menu_always_inject2}{/if}
{if $logged_in != 1 && isset($mm_menu_notlogged_inject)}{$mm_menu_notlogged_inject}{/if}
</ul>
Or you can add programmatically class="active" (or selected) to the current selected menu and do this:
.nav li a.active {
color: #FFFFFF;
}
#ChrisHerbert your solution will not work... you will change all the li of the menu... because the class is in your body tag. (EDIT: the solution was changed, see comments)
With #ChrisHerbert answer, you can do it in two ways:
1) with Javascript, take the class in the body tag then select the one with the associate index (:eq() in jQuery). (you can find a way without javascript for non-javascript user)
OR
2) you can do: .home .nav li:nth-child(0) {}, .about-us .nav:nth-child(1) {}, etc. if you know the index of each page in your menu! Or other child selector but, old versions of IE don't like it!
I think you should do it with my solution rather then the body tag. Still, it is really useful to have that class in the body for page specific thingy to add.
Add a unique class to the <body> tag of each page. For example, on the home page:
<body class="home">
On the contact page: <body class="contact">
On the blog page: <body class="blog">
..and so on.
Then, in your CSS, do something like this:
.home .nav li.home, .contact .nav li.contact, .blog .nav li.blog {
// styling to indicate active state
}
I think the question is, are you looking to have this done dynamically? Or are you coding each page? The other two solutions are great, but a bit overkill if actually you're accessing each page individually. You could just add a class to the selected nav element depending on the page. This is probably the easiest to get your head around if you've not done it before, but #ChrisHerbert's solution is the nicest way of doing it dynamically just using CSS (no PHP ifs etc).
HTML
<div class="nav">
Home
About us
Portfolio
</div>
CSS
.nav a {
color:#ff4444;
}
.nav a.selected {
color:#ff44ff;
}
EDIT: Just realised that #AnnieCaron's answer is the same as mine.

Categories

Resources