How to display items of a particular div on mouseover - javascript

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/

Related

If one element in an array is clicked then add a class to the same nth other array

I want an item to show when a link is clicked. I have made an array of all of the links.
I want to add the class "show" to an array of <div>s. Potentially if the solution makes more sense the <div>'s could be within the anchor, it doesn't matter they will overlay the screen.
HTML
<div class="grid-container">
<a href="#"> <!-- lot's of <a>'s will make the array -->
<div class="grid-item">
<div class="grid-item--inner-container">
<div class="grid-item--normal-content">
<div class="grid-item--heading">
<h2>Lending Creativity</h2>
</div>
<div class="grid-item--border"> </div>
</div><!-- end normal content -->
</div><!-- Grid-item-inner -->
</div><!-- Grid item -->
</a>
~
Second part of the HTML.
<div class="popup-container hidden"><!-- many -->
<div class="grid-item--popup-content" id="lending-creativity--popup">
<button class="close-button"><i class="fa fa-times"></i><span class="close-button--text-hidden">Close</span></button>
</div>
</div>
~
JavaScript
let griditems = document.getElementsByClassName('grid-container')[0].getElementsByTagName('a');
let popupitems = document.getElementsByClassName('popup-container');
let closebuttons = document.getElementsByClassName('close-button');
document.addEventListener("DOMContentLoaded", function() {
for (let i = 0; i < griditems.length; i++) {
griditems[i].addEventListener("click", function () {
popupitems[i].style.display = "flex";
})
}
for (let i = 0; i < griditems.length; i++) {
closebuttons[i].addEventListener("click", function () {
closebuttons[i].style.display = "none";
})
}
})
I'm getting an error saying closebuttons is not defined. I guess I'm not 100% sure I'm doing this the right way either, is this the best way to solve this problem?
The issue was closebuttons[i] is undefined. There were more griditems than close buttons.
I could also simplify my JavasScript as the loops should have been the same length.
let griditems = document.getElementsByClassName('grid-container')[0].getElementsByTagName('a');
let popupitems = document.getElementsByClassName('popup-container');
let closebuttons = document.getElementsByClassName('close-button');
document.addEventListener("DOMContentLoaded", function() {
for (let i = 0; i < griditems.length; i++) {
griditems[i].addEventListener("click", function () {
popupitems[i].style.display = "flex";
})
closebuttons[i].addEventListener("click", function () {
closebuttons[i].style.display = "none";
})
}
})
In the end I decided that instead of changing the style I could just add a class or remove it which would then keep my CSS in a CSS/SCSS file. I found this more maintainable and non-tech staff members would could write CSS could easily change this if it was ever needed in the future. It was also easier to maintain if we ever decided to use block instead of flex or something similar. We could just edit it easily in the inspect element to see the outcomes.
Again my code could probably be simplified by not doing 2 loops. But just in case they were different lengths I kept them in their own loops.
Here's my final code.
let griditems = document.getElementsByClassName('grid-item--link');
let popupitems = document.getElementsByClassName('popup-container ');
let closebuttons = document.getElementsByClassName('close-button');
document.addEventListener("DOMContentLoaded", function() {
for (let i = 0; i < griditems.length; i++) {
griditems[i].addEventListener("click", function () {
popupitems[i].classList.remove("hidden");
popupitems[i].classList.add("shown");
})
}
for (let i = 0; i < closebuttons.length; i++) {
closebuttons[i].addEventListener("click", function () {
popupitems[i].classList.add("hidden");
popupitems[i].classList.remove("shown");
})
}
})

jQuery Replace With only works once for an object reference

I'm having a strange issue with replaceWith (or more likely with object referencing).
I am trying to create a kind of table of rows that either have empty slots or full slots. As a demonstration I made this simple fiddle. http://jsfiddle.net/Ltxtvyn3/3/ In this fiddle 4 empty slots are initialized. Then one is filled. Then the same one should be emptied. But instead it is remaining filled. It is as if I can only use replaceWith once, or I am not understanding something about my object references.
HTML
<div class = "slot empty">Empty</div>
<div class = "slot full">Full</div>
<div class = "wrapper"></div>
CSS
.slot{
width:50px;
height:50px;
display:none;
}
.empty{
background-color:red;
}
.full{
background-color:blue;
}
Javascript
var wrapper = $('.wrapper');
var empty = $('.slot.empty');
var full = $('.slot.full');
var slots = {};
for(var i = 0; i < 4; i++){
slots[i] = empty.clone().show();
wrapper.append(slots[i]);
}
function fillSlot(id){
slots[id].replaceWith(full.clone().show());
}
function emptySlot(id){
slots[id].replaceWith(empty.clone().show());
}
fillSlot(1);
emptySlot(1);
I am hoping that the object var slots maintains a reference to the divs and I'm not sure if it is doing that or not.
No, it's not keeping a reference, but you can fix this pretty easily.
Here's some running code:
var wrapper = $('.wrapper');
var empty = $('.slot.empty');
var full = $('.slot.full');
for(var i = 0; i < 4; i++){
wrapper.append(empty.clone().show());
}
function fillSlot(id){
$(".wrapper .slot").eq(id).replaceWith(full.clone().show());
}
function emptySlot(id){
$(".wrapper .slot").eq(id).replaceWith(empty.clone().show());
}
fillSlot(1);
setTimeout(function() {
emptySlot(1);
}, 2000);
.slot{
width:50px;
height:50px;
display:none;
}
.empty{
background-color:red;
}
.full{
background-color:blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class = "slot empty">Empty</div>
<div class = "slot full">Full</div>
<div class = "wrapper"></div>
Thanks for the answers. I know understand why the object doesn't keep a reference, and I really wanted that to be the case. I simply added a wrapper slot and then I will affect the contents of the wrapper. That way I always have a reference to the slot.
HTML
<div class="slot-content empty">Empty</div>
<div class="slot-content full">Full</div>
<div class = "slot"></div>
<div id="wrapper"></div>
Javascript
var wrapper = $('#wrapper');
var slot = $('.slot');
var empty = $('.slot-content.empty');
var full = $('.slot-content.full');
var slots = {};
for(var i = 0; i < 4; i++){
slots[i] = slot.clone().show();
slots[i].html(empty.clone().show());
wrapper.append(slots[i]);
}
function fillSlot(id){
slots[id].html(full.clone().show());
slots[id].find('.slot-content').html('hello');
}
function emptySlot(id){
slots[id].html(empty.clone().show());
}
fillSlot(1);
emptySlot(1);
fillSlot(2);
UPDATED
Your code work fine just if you change the selection method and you don't want slots list no more.
Replace :
function fillSlot(id){
slots[id].replaceWith(full.clone().show());
}
function emptySlot(id){
slots[id].replaceWith(empty.clone().show());
}
BY :
function fillSlot(id){
wrapper.children().eq(id).replaceWith(full.clone().show());
}
function emptySlot(id){
wrapper.children().eq(id).replaceWith(empty.clone().show());
}
Selecting directly from wrapper what means selecting from fresh DOM. that will fix the problem, take a look at updated fiddle bellow.
Updated JSFiddle
The problem is slots[i] isn't pointing to the div - so replaceWith won't pick the right item. Update the loop as follows (adding slots[i] = wrapper.find(':last-child') ):
for(var i = 0; i < 4; i++){
slots[i] = empty.clone().show();
wrapper.append(slots[i]);
slots[i] = wrapper.find(':last-child')
}
Actually this may make the code a little easier to understand (replace loop with this instead)
for(var i = 0; i < 4; i++){
wrapper.append(empty.clone().show());
slots[i] = wrapper.find(':last-child')
}
Tested and works on FF..
It's not keeping a reference to the DOM element. If you still want to use the array, then you can just repopulate the list every time you update one of its elements. Not terribly efficient, but I suppose it saves you from keeping state in the DOM.
function redraw() {
$('.wrapper').empty();
for(var i = 0; i < 4; i++){
wrapper.append(slots[i]);
}
}
JSFiddle

Fill parent div based on child height/width

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 .

Hiding list items with a "show more" button

I have an issue. I am getting data from a MySQL database, and make a list of it. That's all good, and works fine, but the list is now over 100 items long if I don't limit it.
I've tried Googling how to shorten list, and found some things with jQuery and JavaScript, but that didn't work too well.
What I'm looking for is a way to make the list limit itself on 10 items, with a [More] button under it. When pressed, the next 10 items show, and when pressed again, 10 more etc.
I have my list in normal <li> and <ul> bits.
If there's any more information needed, please ask me. This is the webpage it's about: http://lolmewn.nl/stats/
A bit of my PHP code:
echo "<li><a href=\"?player=" . $row['player'] . "\">" . $row['player'] .
"</a></li>\n";
Maybe you can try this. In this example I used 2 items instead of 10. I used css to hide all li elements starting from the 3rd li element inside the ul. I used jQuery to reveal additional 2 lis every time show more is clicked.
Hope this helps
Updated Link Again...
EDIT
$(function () {
$('span').click(function () {
$('#datalist li:hidden').slice(0, 2).show();
if ($('#datalist li').length == $('#datalist li:visible').length) {
$('span ').hide();
}
});
});
ul li:nth-child(n+3) {
display:none;
}
ul li {
border: 1px solid #aaa;
}
span {
cursor: pointer;
color: #f00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<ul id="datalist">
<li>dataset1</li>
<li>dataset1</li>
<li>dataset2</li>
<li>dataset2</li>
<li>dataset3</li>
<li>dataset3</li>
<li>dataset4</li>
<li>dataset4</li>
<li>dataset5</li>
<li>dataset5</li>
</ul>
<span>readmore</span>
One method is to use ajax to load the list items & restrict them to 10 items using mysql limit.
Otherwise, if you load all at once, you can do the following: (write the code yourself)
Load all of them in a ul and make the display of all none.
Then using jquery eq selector display the first 10 li elements.
on clicking more, just toggle those li which you want to display.
If you want this is pure javascript I made a example on jsfiddle
Javascript
function showMore() {
var listData = Array.prototype.slice.call(document.querySelectorAll('#dataList li:not(.shown)')).slice(0, 3);
for (var i=0; i < listData.length; i++)
{
listData[i].className = 'shown';
}
switchButtons();
}
function showLess() {
var listData = Array.prototype.slice.call(document.querySelectorAll('#dataList li:not(.hidden)')).slice(-3);
for (var i=0; i < listData.length; i++)
{
listData[i].className = 'hidden';
}
switchButtons();
}
function switchButtons() {
var hiddenElements = Array.prototype.slice.call(document.querySelectorAll('#dataList li:not(.shown)'));
if(hiddenElements.length == 0)
{
document.getElementById('moreButton').style.display = 'none';
}
else
{
document.getElementById('moreButton').style.display = 'block';
}
var shownElements = Array.prototype.slice.call(document.querySelectorAll('#dataList li:not(.hidden)'));
if(shownElements.length == 0)
{
document.getElementById('lessButton').style.display = 'none';
}
else
{
document.getElementById('lessButton').style.display = 'block';
}
}
onload= function(){
showMore();
}
HTML
<ul id="dataList">
<li class="hidden">One</li>
<li class="hidden">Two</li>
<li class="hidden">Three</li>
<li class="hidden">Four</li>
<li class="hidden">Five</li>
<li class="hidden">Six</li>
<li class="hidden">Seven</li>
<li class="hidden">Eight</li>
<li class="hidden">Nine</li>
<li class="hidden">Ten</li>
<li class="hidden">Eleven</li>
</ul>
<input id="moreButton" type="button" value="More" onclick="showMore()"/>
<input id="lessButton" type="button" value="Less" onclick="showLess()"/>
CSS
.shown{
display:block;
}
.hidden{
display:none;
}
Have you ever try jquery datatable yet?
Simple solution in pure javascript:
var ul = document.getElementsByTagName("ul")[0], //Your <ul>
readmore = document.createElement("li"),
lisColl = ul.getElementsByTagName("li"),
len = lisColl.length,
lis = [],
pos = 0;
readmore.textContent = "Read more";
for (var i = 0; i < len; i++) {
lisColl[i].style.display = "none";
lis.push(lisColl[i]);
}
readmore.onclick = function () {
if (this.parentNode) {
this.parentNode.removeChild(this);
}
for (var c = 0; pos < len; pos++) {
if ((c++) === 10) {
ul.insertBefore(this, lis[pos + 1]);
break;
}
lis[pos].style.display = "";
}
}
readmore.onclick.call(readmore);
If you want to limit the number of results from the database, add LIMIT 10 (or any number) to the MySQL query.
If you want to actually hide the lists, but leave them available, you will need CSS to initially hide them, and Javascript/Jquery to unhide them. (CSS3 might let you unhide them without Javascript/Jquery, but it isn't fully supported everywhere yet).
Assuming all the list items have the same CSS class then a javascript loop like the following may work:
function unhide(number) {
var items = document.getElementsByClassName('tagnamehere');
var shown=0;
for (var i=0; shown<number && i<items.length; i++) {
if (items[i].style.display=="" || items[i].style.display=="none") {
items[i].style.display="list-item";
shown+=1;
}
}
}
In the CSS, all you need to add is .tagnamehere {display:none;}
Feel free to substitute with your own tags.

Accessing DOM elements without id

i have a page around 500 div as below.
<div onclick='test()' class='test>
<ul class='innermenu'>
<li>1</li>
.....
</ul>
</div>
when the test function is called it need to hide the menu (innermenu) who calls that function.
my problems are
uniquely identify the div without using id
How to hide only the particular ul alone.
OK, first the quick fix, though it is not the best way to use JS on your page:
Change the call to this:
<div onclick="test(this);" class="test">
Then, in test, use this:
function test(el){
var uls = el.getElementsByTagName('ul');
for(var i = 0; i < uls.length; i++){
if(uls[i].className == 'innermenu'){
uls[i].style.display = "none";
break;
}
}
}
This will hide just the child ul of the div that is clicked.
A better way
OK, for the longer answer. Either attach the events after the fact using attachEvent and addEventListener or use a library like jQuery to help you out. Here is the raw solution:
Set up your HTML this way (no onclick):
<div class="test">
And then at the very end of your HTML put this:
<script type="text/javascript">
var divs = document.getElementsByTagName('div');
function test(){
var uls = this.getElementsByTagName('ul');
for(var i = 0; i < uls.length; i++){
if(uls[i].className == 'innermenu'){
uls[i].style.display = "none";
break;
}
}
};
for(var i = 0; i < divs.length; i++){
var div = divs[i];
if(div.className !== "test") continue;
if(window.addEventListener){
div.addEventListener( 'click', test, true ); //FF, Webkit, etc
} else if (window.attachEvent) {
div.attachEvent('onclick', test); // IE
} else {
div.onclick = test; // Fallback
}
}
</script>
Now, you don't have JavaScript code in your HTML, and you can get rid of the extra parameter on the test function.
There is a method
document.getElementsByClassName
but it isn't supported in all browsers.
javascript
function test(elem)
{
var childElem = elem.children[0];
childElem.style.display = 'none';
}
<div onclick='test(this)' class='test'>
<ul class='innermenu'>
<li>1</li>
<li>2</li>
</ul>
</div>
If you can use jQuery then you can do something like this
$("div.test").click(function(){
$(this).find("ul.innermenu").hide();
});
If you don't want assign ids, you can try this to hide the div which gets clicked:
<div onclick="hideMe(this);" class='test>
<script>
function hideMe(elem)
{
elem.style.display = 'none';
}
</script>
Try passing "this" as parameter:
<div onclick='test(this)' class='test>
<ul class='innermenu'>
<li>1</li>
.....
</ul>
function test(sender) {
//sender is DOM element that is clicked
alert(sender.id);
}
If getElementsByClassName is not supported by all browsers as mentioned by #rahul, you can iterate through the dom and find it yourself - provided there is only one <ul> with class name "innermenu"
var uls = document.body.getElementsByTagName("ul");
var len = uls.length;
for(var i = 0; i < len; i++)
{
var ul = uls.item(i);
if(ul.getAttribute("class") == "innermenu")
{
ul.style.display = "none";
break;
}
}

Categories

Resources