Predict element width with JavaScript - 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/

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.

sub-menu expansion and innerHTML replacement based on onclick, using just 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>

Jquery: Navigating in menu using tab

I want to find a general rule that will help me navigate in menus, using tab. I know how to catch the tab event but there are several problems:
There are drop-down menus that i can't accese only be pressing tab key.
The structure of the drop-down menus is not standard. i.e. the hidden ul elements may be inside other elements (e.g div) or may be nested drop-down menus inside other drop-down menus
The events that make the hidden menus to be visible/invisible vary. Click and hover events are the most popular.
Can someone help me?
You can use the tabindex attribute on the list item that triggers the showing of your dropdown. This wil make the parent li focusable by pressing tab. You can then, with some CSS and Javascript, apply rules to children to make them visible.
About the fiddle:
Note that I use the opacity and pointer-events properties in CSS to switch states of the dropdown. When an element has visibility: hidden or display: none applied, it will not be focusable at all. Since the tab key by default brings focus to the next focusable element, your target element should not have either of those properties set to the shown values at the point the tab key is being pressed.
Also, you'll have to accurately keep track of the tabindexes throughout your navigation. First is the first main anchor, then the enclosing list-item, to make the dropdown visible. Then come the anchors inside that. (this is the point where JS has to take over from CSS) This brings us at tabindex 5 when the 3 subItems inside are assigned a tabindex, so we'll continue counting from 6 for the next main item's direct <a> child, and so forth.
You'll have to figure out the way to make your multi-level dropdowns work as expected yourself, but this is a starting point. You could additionally check in your script for arrow key presses, and give another element focus in response.
Fiddle:
$(function() {
$('nav').on('focus', '.dropdown a', function() {
$(this).closest('.mainItem').addClass('focus');
}).on('blur', '.dropdown a', function() {
$(this).closest('.mainItem').removeClass('focus');
});
});
nav > ul {
display: flex;
}
nav ul {
list-style: none;
margin: 0;
padding: 0;
}
nav .mainItem {
position: relative;
margin: 0 5px;
padding: 0.5rem;
background-color: #eee;
}
nav .dropdown {
position: absolute;
width: 100%;
left: 0;
top: 100%;
opacity: 0;
pointer-events: none;
}
nav .mainItem:hover .dropdown,
nav .mainItem:focus .dropdown,
nav .mainItem.focus .dropdown,
nav .mainItem > a:hover .dropdown,
nav .mainItem > a:focus .dropdown
nav .mainItem.focus .dropdown {
opacity: 1;
pointer-events: all;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
<ul>
<li class="mainItem" tabindex="2">Parent 1
<ul class="dropdown">
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ul>
</li>
<li class="mainItem" tabindex="7">Parent 2
<ul class="dropdown">
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ul>
</li>
</ul>
</nav>

Variable Width Media Queries [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have done quite a bit of digging, and can't seem to find how people handle content restructuring for a variable width element.
For example, if I have a dynamically created horizontal menu it may only have 3 items..
<div>
<ul>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</div>
And this menu will only have a small width, let's say 400px. I can create a media query to adjust the way it is displayed when the window falls below 400px, however..
If a user adds another item..
<div>
<ul>
<li>Home</li>
<li>About</li>
<li>Location</li>
<li>Contact</li>
</ul>
</div>
Suddenly this menu is larger then 400 px, and so on. My question is, how can I structure my code to handle a variable element width and still control the way that is displayed?
EDIT: When I re-size the browser window on my horizontal menu, at a certain variable width, the inline-block li elements drop below the rest of the menu. Instead of letting each element drop as the screen is compressed I would prefer to make the entire menu drop to a vertical orientation. I cannot simply use a media query, since there are variable amounts of menu items. To illustrate the issue try re-sizing the example code in this fiddle: https://jsfiddle.net/f5Lv73hp/
I don't understand your question, so, consider editing your post with more information, including what do you espect...
By the way:
Horizontal Menu, if you need to keep all list-items with the same width, you can use display-table, there aren't any javascript requirements, just set the list as a table ( see .menu-horizontal css ).
function CasesCtrl($) {
var case1 = $('#case1');
$('button', case1).click(function() {
var list = $('ul', case1);
var len = $('li', list).length;
var newItem = '' +
'<li class="menu-item">' +
'<a class="menu-item-link">Item '+ (len + 1) +'</a>' +
'</li>'
;
list.append(newItem);
});
}
jQuery(document).ready(CasesCtrl);
article {
width: 100%;
padding: 2px;
border: 1px solid black;
margin-bottom: 2em;
overflow: hidden;
}
.menu {
list-style: none;
margin: 0;
padding: 0;
}
.menu-item {
}
.menu-item-link {
background: lightseagreen;
margin: 2px;
padding: 2px 5px;
display: block;
text-transform: uppercase;
line-height: 1;
}
.menu-horizontal {
display: table;
}
.menu-horizontal .menu-item {
display: table-cell;
width: 1%;
white-space: nowrap;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<article id="case1">
<ul class="menu menu-horizontal">
<li class="menu-item">
Item 1
</li>
<li class="menu-item">
Item 2
</li>
<li class="menu-item">
Item 3
</li>
</ul>
<button type="button">Add Menu Item</button>
</article>
Be more specific and I'll edit my answer as you need!
Are you looking for something like this? https://jsfiddle.net/4p18mxg9/2/
I am using javascript to get the width of the ul and applying the width to to media query, that way when you add more li it is not dependent on the content.
var width = document.getElementById('ul').offsetWidth;
document.querySelector('style').textContent +=
"#media screen and (max-width:" + width + "px) { li{float: none; width: 100%; background-color: blue; color: white;}}"
Added some color, to see easier: https://jsfiddle.net/4p18mxg9/3/

Changing the direction in which an ul expands when a button is clicked

So I got a button which when clicked, toggles the visibility of an ul. My button element is a child of my footer, which is fixed to the bottom of my screen. The issue I am having is that when toggled, the ul expands downward instead of up. How would I go about making my ul expand upwards with the first li item starting from the bottom, with each succeeding li item stacking on top of the one before it.
Here is my code:
<footer class="web-stream">
<button type="button" class="active-stream">All</button>
<ul class="feed-list">
<li>feed 1</li>
<li>feed 2</li>
</ul>
</footer>
CSS
.web-stream {
background: green;
width: 100%
height: 2.3em;
margin: auto;
position: fixed;
bottom:0%;
left:0px;
right:0px;
}
.active-stream {
width: 9.5em;
height: 2.3em;
text-align: center;
}
.feed-list {
display: none;
}
Jquery:
jQuery(document).ready(function(){
jQuery(document).on('click','.active-stream', function(event) {
jQuery('.feed-list').toggle('fast'); //use 'slow'
});
});
Here's a JSFiddle.
If I understand your question properly, you just need to reverse the order of the list. If the content is dynamically generated then you could do this when you output the <li> tags. Otherwise the JS in this JSFiddle can accomplish the same thing.
I also moved the button to appear below the list to match your drawing.

Categories

Resources