Fill parent div based on child height/width - javascript

I'm trying to create a expanding menu that appends the parent div with multiple 400px smaller divs until the full parent div is filled.
In the for loop i'm checking if .background is the same height as .main-nav, if it isn't then continue to add the .slice divs.
The issue I'm having is that once .background is the same height as .main-nav it stops adding the .slices divs which means the full width isn't covered.
P.S First 'real'JS project; sorry if my code is messy.
The following JSFiddle will make more sense:
http://jsfiddle.net/8ryAD/13/
JS:
(function () {
'use strict';
var s = document.getElementsByClassName('slice');
var m = document.getElementById('btn-nav');
var b = document.getElementsByClassName('background')[0];
var a = document.getElementById('main-nav');
var w = document.documentElement.clientHeight;
m.addEventListener('click', function() {
m.classList.add('open');
for(var i = b.clientHeight; i < a.clientHeight; i++) {
var c = document.getElementsByClassName('slice')[0];
var d = c.cloneNode(true);
if(b.clientHeight == a.clientHeight) {
break;
} else {
c.parentNode.appendChild(d);
}
}
for(var i = 0; i < s.length; i++){
s[i].style.opacity = 1;
}
}, false);
}());
HTML
<div id="master">
<a id="btn-nav">
<span></span>
<span>Menu</span>
<span></span>
</a>
<nav id="main-nav">
<div class="background" >
<div class="slice"></div>
</div>
</nav>
</div><!-- end master -->

The main problem in your code is the for loop. For that particular case you should use a while loop. For loop are used when you know precisely the number of iterations you are going to do.
I reworked the loop this way :
// Loop while we don't cover client surface
while(dsWidth < a.clientWidth*(a.clientHeight / c.clientHeight)) {
var d = c.cloneNode(true);
c.parentNode.appendChild(d);
dsWidth = dsWidth + d.clientWidth;
}
Here is a fiddle doing the job. It is not clear if you want to add "slices" vertically or horizontally. I chose horizontally but you should have no difficulties .

Related

In pure JS, I am trying to set two similar classes but with different numbers or to set the same class to two different elemnts

I tried to set the same class imagens-giratorias to two elements or to set imagens-giratorias and imagens-giratorias-2. The class worked in first element, and the same class stopped of animating in the second element.
[I provide the JSFiddle at the end.]
Check the #rafaelcastrocouto's original code at https://stackoverflow.com/a/59524483/8041366. If you prefer reading the complete code here, here is the code taken from there, but with a bit modified:
var counter = 1;
var div = document.querySelector('.imagens-giratorias');
var imagens = document.querySelectorAll('.imagens-giratorias img');
var showNext = function () {
counter++;
if (counter > 3) counter = 1;
div.classList.remove('imagem1', 'imagem2', 'imagem3')
div.classList.add('imagem'+counter);
};
for (var img of imagens) {
img.addEventListener('animationend', showNext);
}
And small CSS snippet:
<div class="section-2">
<div class="item-2">
<div class="imagens-giratorias imagem1">
</div>
</div>
</div>
<div class="section-3">
<div class="item-2">
<div class="imagens-giratorias imagem1">
</div>
</div>
Or
<div class="section-3">
<div class="item-2">
<div class="imagens-giratorias-2 imagem1">
</div>
</div>
1st solution, that same original code above I am referring.
2nd solution:
var div = document.querySelector('.imagens-giratorias, .imagens-giratorias-2');
var imagens = document.querySelectorAll('.imagens-giratorias img, .imagens-giratorias-2 img');
3rd solution
var div = document.querySelector('[class^=imagens-giratorias]');
var imagens = document.querySelectorAll('[class^=imagens-giratorias] img');
4th solution
const contador = 1;
const div = document.querySelector('.imagens-giratorias');
const imagens = document.querySelectorAll('.imagens-giratorias img');
I also tried to use from multiple selectors with document.querySelectorAll. No luck.
But all these solutions did not work.
JSFiddle
Please pay attention to two elements. While one element will always animate, another will stop of animating.
https://jsfiddle.net/gusbemacbe/mbp84u6r/2/
If I understand you correctly you're trying to grab elements that have a class name starting with imagens-giratorias. If that's the case, use the ^ attribute selector as shown below:
document.querySelectorAll("[class^="imagens-giratorias"]")
Update:
Based on your update it appears that only one of your two divs' images is animating but in reality they're stacked on top of each of other. Feel free to use whatever layout method you want but for demonstration's sake I floated one left and the other right. Other that it was a matter of looping through your divs and assigning your function to their child images as so:
var divs = document.querySelectorAll('.imagens-giratórias');
var contador = 1;
var mostrarPróximo = function(div) {
contador++;
if (contador > 3) contador = 1;
div.classList.remove('imagem1', 'imagem2', 'imagem3')
div.classList.add('imagem' + contador);
};
Array.from(divs).forEach(function(div, index) {
var images = div.querySelectorAll('img');
Array.from(images).forEach(function(img) {
img.addEventListener('animationend', mostrarPróximo.bind(null, div));
});
});
https://jsfiddle.net/1r6yjf5s/

Resize set of List Elements to width of widest list element?

I'm trying to get my list elements in my pure css dropdown to be uniform size based on the width of the largest element in the ul. This is the current html:
<nav>
<ul id="drop-nav">
<li>Home</li>
<li onmouseover="SizeChildren('DropDown1')">Filler
<ul id="DropDown1">
<li>Filler1</li>
<li>Filler223456</li>
<li>Filler212314235234523</li>
</ul>
</li>
</ul>
<nav>
I run the SizeChildren function passing the parent containers id to the function (note: I realize there is redundant code in here, it's stuff I was messing with):
function SizeChildren(ElementID)
{
var WidthArray = new Array();
UOList = document.getElementById(ElementID);
WidthArray = UOList.getElementsByTagName('li');
var MaxWidth;
for(i = 0; i < WidthArray.length; i++)
{
if(i == 0)
{
MaxWidth = WidthArray[i].offsetWidth;
}
else
{
var curr = WidthArray[i].offsetWidth;
if(curr > MaxWidth)
{
MaxWidth = curr;
}
}
}
var nodes = document.getElementById(ElementID).childNodes;
for(i = 0; i < nodes.length;i++)
{
if(nodes[i].nodeName.toLowerCase() == 'li')
{
nodes[i].style.width = MaxWidth;
}
}
}
There are two problems I am running into with the current code, first off, it is calculating the max width to be 82 (should be closer to 150 or close to it) and it is not actually setting the widths, meaning that nothing is actually changing on the page, and that I have no clue about, even with the max width getting miscalculated this portion of the code should still work, so what am I missing? I tried using .offsetWidth as well and that still produced no fruit.
I can't use JQuery, flex boxes, or anything like that, it has to be done in JavaScript if possible.

How to display items of a particular div on mouseover

I have the div structure
<div id="navigate">
<div class="menu">
<div class="group">Mail</div>
<div class="item">Folders</div>
<div class="item">Messages</div>
</div>
<div class="menu">
<div class="group">Contacts</div>
<div class="item">Friends</div>
<div class="item">Work</div>
</div>
<div class="menu">
<div class="group">Setting</div>
<div class="item">General</div>
<div class="item">Account</div>
</div>
</div>
Right now all items are hidden, and only divs with class 'group' is shown. What I would like to do is if I mouse over a specific menu div, only items of that menu would appear.
Right now I have this code:
function initialise()
{
hideAllItems();
setMouseOvers();
}
function hideAllItems()
{
var nav = document.getElementById("navigate");
var items = nav.getElementsByClassName("item");
for(var i = 0; i < items.length; i++)
{
items[i].style.visibility = "hidden";
items[i].style.display = "none";
}
}
function setMouseOvers()
{
var nav = document.getElementById("navigate");
var menuArr = nav.getElementsByClassName("menu");
for(var x = 0; x < menuArr.length; x++)
{
var itemArrs = menuArr[x].getElementsByClassName("item");
/*var show = function(){ show(itemArrs); };
var hide = function(){ hide(itemArrs); };*/
menuArr[x].onmouseover=function(){ show(itemArrs); };
menuArr[x].onmouseout=function(){ hide(itemArrs); };
}
}
function show(itemArr)
{
for(var i = 0; i < itemArr.length; i++)
{
alert(itemArr[i].innerHTML);
itemArr[i].style.visibility = "visible";
itemArr[i].style.display = "block";
}
}
function hide(itemArr)
{
for(var i = 0; i < itemArr.length; i++)
{
itemArr[i].style.visibility = "hidden";
itemArr[i].style.display = "none";
}
}
And this works, thought it only displays General and Account no matter which menu I hover over. I vaguely understand whats going wrong, but I can't see anyway to fix it. Any ideas? I do not want to change the html structure (e.g. add ids, or create specific classes) if i can help it!
I know that you most probably are looking for a javascript solution, but you could use a simple CSS solution:
.group:hover ~ .item {
display: block;
}
Working Fiddle
But be aware that it is not supported by older IE (< 8) browsers SUPPORT. It depends on your target group if you want to use it.
Why not simply using CSS: DEMO
.menu .item{
display:none;
}
.menu:hover .item{
display:block;
}
As you ask for an JavaScript Only solution (no change in HTML/css) i suggest the following:
The problem is using "itemArrs" in an anonymous function, as only the latest written "itemArrs" is used for all of them, use "this" instead.
for example:
...
groups[x].onmouseover=function(){ show(this); };
...
and
function show(item) {
var items = item.parentNode.getElementsByClassName("item");
...
A complete JS-only solution that works can be found here:
http://jsfiddle.net/Wn4d4/3/

targeting child of event.currentTarget

I found a simple tab-menu jQuery plugin that needs some adapting for a project of mine. The tabs in question - being absolutely positioned - are taken out of the flow and as such don’t contribute to the wrapping div’s height, so the background behind them don’t show.
I am trying to force the height of the wrapping div (containing the background image) to match the height of the selected tab (+400px for nav and header) and to achieve that on the fly I am adapting the original jQuery file.
Here is the code (with the few extra lines (commented ‘added!’) of mine).
var cbpHorizontalMenu = (function () {
var $listItems = $('#cbp-hrmenu > ul > li'),
$menuItems = $listItems.children('a'),
$body = $('body'),
current = -1;
function init() {
$menuItems.on('click', open);
$listItems.on('click', function (event) {
event.stopPropagation();
});
}
function open(event) {
if (current !== -1) {
$listItems.eq(current).removeClass('cbp-hropen');
}
var $item = $(event.currentTarget).parent('li'),
idx = $item.index();
if (current === idx) {
$item.removeClass('cbp-hropen');
//added!
current = -1;
} else {
$item.addClass('cbp-hropen');
current = idx;
$body.off('click').on('click', close);
var content2Height = jQuery(".cbp-hrsub").height() + 400;
jQuery('#content2').height(content2Height); //added
}
return false;
}
function close(event) {
$listItems.eq(current).removeClass('cbp-hropen');
//added!
current = -1;
}
return {
init: init
};
})();
It does something, but not what I need. It gets the height of the first div.cbp-hrsub and applies it (+400px) to the div.content2. What I need is to target the current tab (a child of event.currentTarget, me thinks?), to calculate its height and to apply it to the content2 div.
This is a simplified HTML if that helps:
<div class="content2">
<nav id="cbp-hrmenu" class="cbp-hrmenu">
<ul>
<li>
tab 1
<div class="cbp-hrsub">
<div class="cbp-hrsub-inner">
I am 1st tab, 100px height.
</div>
</div>
</li>
<li>
tab 2
<div class="cbp-hrsub">
<div class="cbp-hrsub-inner">
I am 2nd tab, 200px height.
</div>
</div>
</li>
<li>
Nigel's CV
<div class="cbp-hrsub">
<div class="cbp-hrsub-inner">
I am 3rd tab, 300px height.
</div>
</div>
</li>
</ul>
</nav>
Just to clarify, I want to keep the original plugin as it is, just to insert something instead of my 2 lines, towards the end of file. (var content2Height = jQuery(".cbp-hrsub").height() + 400;jQuery('#content2').height(content2Height); //added
Thanks everyone for their time.
Zel
I get inconsistent results when targeting a parent-container with .parent(). In this line:
var $item = $(event.currentTarget).parent('li'),
idx = $item.index();
Try instead to use .closest():
var $item = $(event.currentTarget).closest('li'),
idx = $item.index();
Oh, wait! I see the issue:
var content2Height = jQuery(".cbp-hrsub").height() + 400;
You are retrieving all the .cbp-hrsub classed elements, here. It's going to try to return a height, and I'm not exactly certain how jQuery determines that when it's looking at an array, but I'm guessing it just picks the first element out of the array.
What you're really needing at this point, then, is something like this:
var content2Height = $item.first(".cbp-hrsub").height() + 400;
THAT should give you the height of .cbp-hrsub contained within the current item (found above), and not that of the first .cbp-hrsub in the array.

Find respective divs, create new divs and append the ones that were found

so i am in a bit of a jam here and having a really hard time trying to solve this dilemma.
Here is what i am trying to do:
There is a container div (id=posts) full of other divs (name=posts).
I want it to grab in those divs and, by even/odd position, append them inside 2 other divs.
These 2 other divs are generated by javascript, one floats left and the other floats right.
Then i want it to empty the container div and insert the floating divs.
So far i have managed everything but one thing; it is only using the first found div...
Here is the HTML code:
<div id="posts">
<div name="posts">
left
</div>
<div name="posts">
right
</div>
<div name="posts">
left
</div>
<div name="posts">
right
</div>
<div name="posts">
left
</div>
<div name="posts">
right
</div>
<div name="posts">
left
</div>
<div name="posts">
right
</div>
</div>
Here is the script that i have been working with so far:
<script type="text/javascript">
var container = document.getElementById("posts");
var posts = document.getElementsByName("posts");
var rdiv = document.createElement("div");
rdiv.style.backgroundColor = "#0F0";
rdiv.style.cssFloat = "right";
rdiv.style.clear = "right";
var ldiv = document.createElement("div");
ldiv.style.backgroundColor = "#F00";
ldiv.style.cssFloat = "left";
ldiv.style.clear = "left";
for (i=0; i<posts.length; i++){
if (i%2){
rdiv.appendChild(posts[i]);
}else{
ldiv.appendChild(posts[i]);
}
}
container.innerHTML = "";
container.appendChild(rdiv);
container.appendChild(ldiv);
</script>
Now what this is doing is that it creates the left and right floating divs normally, but it only appends the first div inside both, the rest goes kabluey, can anyone help me with this one?
The main issue you are having is: appending a live element to another element, removes the element from its original location, causing problems with the loop.
To fix this, store the elements into a temporary array, appending them to their corresponding div's only once the looping is done:
var temp1 = [], temp2 = [];
for (i=0; i<posts.length; i++){
if (i%2){
temp1.push(posts[i]);
}else{
temp2.push(posts[i]);
}
}
for(var i = 0; i < temp1.length; i++) {
rdiv.appendChild(temp1[i]);
}
for(var i = 0; i < temp2.length; i++) {
ldiv.appendChild(temp2[i]);
}
You can see the full working example here: http://jsfiddle.net/FqSMw/

Categories

Resources