In one of my projects I made 3 galleries, I would like to put both of them on the same page in the same position, not at the same time, however. For this to be possible, I chose to create 3 buttons. When I click on the first button for example, the first gallery should appear (both galleries are initially on display:none), then when I click on the second button, the second one should appear and the one shown before should disappear, and so for each of the galleries. I made a simplified copy of the page to make the thinking easier.
In general, my problem is that I don't quite know how to apply a function to all the elements in an Array except for one element.
Here is the code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Galleries</title>
<link rel="stylesheet" type="text/css" href="gs.css">
<style type="text/css">
body{
background-color:royalblue;
}
header{
text-align: center;
}
article{
width:95%;
margin:auto 2.5% auto 2.5%;
height:850px;
background-color:tomato;
display:none;
}
</style>
</head>
<body>
<header>
<button>Third Gallery</button>
<button>Second Gallery</button>
<button>Third Gallery</button>
</header>
<section>
<article>
<h1>This is the first gallery</h1>
</article>
<article>
<h1>This is the second gallery</h1>
</article>
<article>
<h1>This is the third gallery</h1>
</article>
</section>
<script type="text/javascript">
var button=document.getElementsByTagName('button');
var gallery=document.getElementsByTagName('article');
for(var i=0; i<button.length; i++){
(function(index){
button[index].onclick=function(){
gallery[index].style.display="block";
}
}(i));
}
</script>
</body>
</html>
You could iterate over all the elements and compare the index of the button with the index of the current gallery item:
[].forEach.call(gallery, function (el, i) {
el.style.display = i === index ? 'block': 'none';
});
or:
for (var i = 0; i < gallery.length; i++) {
gallery[i].style.display = i === index ? 'block': 'none';
}
This will loop over all the elements and set the display of each element to none except for the on with an index that corresponds to the clicked button.
Example Here
var button = document.getElementsByTagName('button');
var gallery = document.getElementsByTagName('article');
for (var i = 0; i < button.length; i++) {
(function(index) {
button[index].onclick = function() {
[].forEach.call(gallery, function (el, i) {
el.style.display = i === index ? 'block': 'none';
});
}
}(i));
}
What you have done is almost right... Loop through the whole thing and when the particular element comes, do not do that, but I don't understand what's the use of closure here:
var button=document.getElementsByTagName('button');
var gallery=document.getElementsByTagName('article');
for(var i=0; i<button.length; i++){
if (i != 2) // Make sure `i` is not equal to 2.
(function(index){
button[index].onclick=function(){
gallery[index].style.display="block";
}
}(i));
}
Related
This question already has answers here:
How to call multiple JavaScript functions in onclick event?
(14 answers)
Closed 5 years ago.
I have two JS functions:
function myFunctionMenu() {
document.getElementById("main-menu").classList.toggle("shows");
}
and
function expandFunction() {
document.getElementById("menu-button").classList.toggle("expand");
}
Both of which toggle classes. Those classes have styles:
.shows {
margin-right: auto;
margin-left: auto;
display: block;
transition: all 2s ease;
}
and
.expand {
padding: 1% 20%;
background-color: #8c8c8c;
}
I then have two more functions, one for each of those original functions, which removes that class when clicking outside of the button that initiates both functions:
window.onclick = function(event) {
if (!event.target.matches('#menu-button')) {
var dropdowns = document.getElementsByClassName("house-menu");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('shows')) {
openDropdown.classList.remove('shows');
}
}
}
}
and
window.onclick = function(event) {
if (!event.target.matches('#menu-button')) {
var dropdowns = document.getElementsByClassName("dropbtn");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('expand')) {
openDropdown.classList.remove('expand');
}
}
}
}
The HTML follows, just in case:
<!doctype html>
<head>
<title>Site Photos</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="Styles/new-menu.css">
<script type="text/javascript" src="script/menu-scripts.js"></script>
</head>
<body>
<br>
<h1>
Name <br>
</h1>
<h2>
Site Photos
</h2>
<!--Menu-->
<div class="menu-container" div style="margin-left:auto; margin right:auto; margin-top:5%; margin-bottom:auto; width:40%;">
<!-- Main Menu Button -->
<div id="main-button-container" style="text-align:center; margin-top:-8%;">
<button onclick="myFunctionMenu();expandFunction()" class="dropbtn" id="menu-button"> Menu </button>
</div>
<hr>
<!-- Main Menu Container -->
<div id="main-menu" class="house-menu">
Main House <hr>
Car Barn <hr>
Utility Building <hr>
Central Plant <hr>
Exteriors<hr>
</div>
<hr>
</div>
</body>
</html>
The problem I am having is that I can only run one of the two "remove when click outside the button" functions at a time.
Can anyone explain to me why?
Thank you.
Because the first window.onclick will get overridden by the second one. Just like writing var a = 1; and later var a = 2;. You should use document.addEventListener for this purpose:
document.addEventListener("click", function(e){
if (!e.target.matches('#menu-button')) {
var dropdowns = document.getElementsByClassName("house-menu");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('shows')) {
openDropdown.classList.remove('shows');
}
}
}
});
and
document.addEventListener("click", function(e){
if (!e.target.matches('#menu-button')) {
var dropdowns = document.getElementsByClassName("dropbtn");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('expand')) {
openDropdown.classList.remove('expand');
}
}
}
});
This question already has an answer here:
JavaScript HtmlCollection loop never returns second element
(1 answer)
Closed 7 years ago.
This should take each element of class="one_level" and change its className to four_level. But this doesn't happen. Only this first list item is getting its appearance changed, the second is still the same.
HTML:
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>DOCUMENT OBJECT MODEL</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="css.css">
</head>
<body>
<ul>
<li class="one_level" style="background-color:black; color:white; margin:5px; ">ONE</li>
<li class="one_level" style="background-color:black; color:white; margin:5px ">TWO</li>
<li class="two_level" style="background-color:orange; color:red; margin:5px">THREE</li>
<li class="two_level" style="background-color:orange; color:red; margin:5px">FOUR</li>
</ul>
<div id="getValues"></div>
<script src="javascript.js"></script>
</body>
</html>
CSS:
.four_level{
border:solid 5px orange;
-webkit-border-radius:5px 6px;
}
.five_level{
border:solid 5px pink;
-webkit-border-radius:5px 6px;
}
JS:
//Get elements for class: one_level
var classOneSet = document.getElementsByClassName("one_level");
var lengthList = classOneSet.length;
//Change appearance
var i;
for (i = 0; i < lengthList; i++){
classOneSet.item(i).className = "four_level";
}
The getElementsByClassName method returns a live list of elements. When you change the class of the first element it will be removed from the list. When you try to change the second element it has moved to the first position in the list and you won't find it at the second position. If you had more than two elements to change, you would see that it changed every other element.
If you loop backwards through the list it works:
window.onload = function(){
//Get elements for class: one_level
var classOneSet = document.getElementsByClassName("one_level");
var lengthList = classOneSet.length;
//Change appearance
var i;
for (i = lengthList - 1; i >= 0; i--){
classOneSet.item(i).className = "four_level";
}
};
.four_level { background: red; }
<ul>
<li class="one_level">ONE</li>
<li class="one_level">TWO</li>
<li class="two_level">THREE</li>
<li class="two_level">FOUR</li>
</ul>
Instead of using item, treat it like a classic array:
var i;
for (i = 0; i < lengthList; i++){
classOneSet[i].className = "four_level";
}
I have 1,000 links on one page. Each link has a title/note/tool-tip. All the titles say the same thing so rather than typing it up on each line is there a way that I can have javscript do this for me?
Example Before:
<div class="links">
Tooltips
Tooltips
Tooltips
Tooltips
</div>
Example of what I would like:
<div class="links">
<a href="#" >Tooltips</a>
<a href="#" >Tooltips</a>
<a href="#" >Tooltips</a>
<a href="#" >Tooltips</a>
</div>
and have java script display a note when mouseover of "div .links a"
Thanks in advance.
jsFiddle Demo
var set = document.querySelectorAll(".links a");
var tip = document.createElement("div");
tip.className = "hover";
var msg = document.createElement("div");
tip.appendChild(msg);
msg.innerHTML = "Generic Hover Message";
for( var i = 0, n = set.length; i < n; i++ ){
set[i].onmouseover = function(){
this.parentNode.insertBefore(tip,this);
};
set[i].onmouseout = function(){
tip.parentNode.removeChild(tip);
};
}
Set up events for the target elements
Use a combination of document.querySelectorAllMDN and iterate through the set assigning the onmouseover eventMDN and onmousout eventMDN to each element.
var set = document.querySelectorAll(".links a");
for( var i = 0, n = set.length; i < n; i++ ){
set[i].onmouseover = function(){
Create an element using document.createElementMDN for the tooltip
var tip = document.createElement("div");
tip.className = "hover";
var msg = document.createElement("div");
tip.appendChild(msg);
msg.innerHTML = "Generic Hover Message";
Create styling to position the tooltip
.hover{
position: absolute;
display:inline-block;
width:100%;
}
.hover > div{
top: 1.2em;
position:absolute;
z-index: 1;
}
Use insertBeforeMDN to place the element
this.parentNode.insertBefore(tip,this);
Like this?
<div class="links" title="This is a title">
<a href="#" >Tooltips</a>
...
Here is an example using Travis' proposed solution with a querySelector and onmouseover.
<html>
<head>
<title>Link over</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<style>
.links{
}
</style>
<script>
// I show a tooltip
function showTip() {
console.log("I'm a tooltip");
}
// I set up the listeners
window.onload = function() {
var links = document.querySelector("div.links");
for (var i = 0; i < links.children.length; i++) {
links.children[i].onmouseover = showTip;
}
};
</script>
</head>
<body>
<div class="links">
Tooltips
Tooltips
Tooltips
Tooltips
</div>
</body>
</html>
I want to change the paragraphs visibility to none or hidden dynamicly using javascript.I have 4 paragraphs and I want only one to be displayed.If a user clicks right button the next parargraph should be displayed instead of the previous one.If left button, then another way.But my event handlers don't seem to respond inside the paragrap_switch function.Please help me with that
HTML
<head>
<style>
p{
border:1px solid black;
width:20%;
display:none;
}
input{
width:40px;
}
</style>
</head>
<body>
<p class="paragraph">TEXT 1</p>
<p class="paragraph">TEXT 2</p>
<p class="paragraph">TEXT 3</p>
<p class="paragraph">TEXT 4</p>
<input type = "button" value = "left" class = "button"/>
<input type = "button" value = "right" class = "button"/>
And Javascript
function paragraph_switch (){
var paragraphs = document.getElementsByClassName('paragraph');
var buttons = document.getElementsByClassName('button');
for(var i = 0;i < paragraphs.length; i++){
if(i >= 0){
if(buttons[0].onclick = function(){
paragraphs[i].style.display = "none";
paragraphs[i+1].style.display = "block";
i++;
}
else if(buttons[1].onclick = function(){
paragraphs[i].style.display = "none";
paragraphs[i-1].style.display = "block";
i--;
}
}
}
}
I am not sure what your code is doing. You are assigning eventhandler inside the if condition.
I have implemented your requirement using jQuery. Here is the jsfiddle.
http://jsfiddle.net/DyZf2/
Here is the js code.
$("p:first").show();
$("#left").click(function(){
$("p:visible").hide().prev().show();
});
$("#right").click(function(){
$("p:visible").hide().next().show();
});
You may have to modify the code to make sure that next/previous paragraph exists before hiding the current one.
I have the following structure:
<div id="container">
<div id="someid1" style="float:right"></div>
<div id="someid2" style="float:right"></div>
<div id="someid3" style="float:right"></div>
<div id="someid4" style="float:right"></div>
</div>
Now someid is acually a unique id for that div. Now i receive an array which has a different order say someid 3,2,1,4, then how do i move these divs around to match the new order using jQuery?
Thankyou very much for your time.
My plugin version - Working Demo
Takes an array and optional id prefix and reorders elements whose ids correspond to the order of (id prefix) + values inside the array. Any values in the array that don't have an element with the corresponding id will be ignored, and any child elements that do not have an id within the array will be removed.
(function($) {
$.fn.reOrder = function(array, prefix) {
return this.each(function() {
prefix = prefix || "";
if (array) {
for(var i=0; i < array.length; i++)
array[i] = $('#' + prefix + array[i]);
$(this).empty();
for(var i=0; i < array.length; i++)
$(this).append(array[i]);
}
});
}
})(jQuery);
Code from the demo
jQuery
$(function() {
$('#go').click(function() {
var order = $('#order').val() == ""? null: $('#order').val().split(",");
$('#container').reOrder(order, 'someid');
});
});
(function($) {
$.fn.reOrder = function(array, prefix) {
return this.each(function() {
prefix = prefix || "";
if (array) {
for(var i=0; i < array.length; i++)
array[i] = $('#' + prefix + array[i]);
$(this).empty();
for(var i=0; i < array.length; i++)
$(this).append(array[i]);
}
});
}
})(jQuery);
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<title>reOrder Demo</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen">
body { background-color: #fff; font: 16px Helvetica, Arial; color: #000; }
div.style { width: 200px; height: 100px; border: 1px solid #000000; margin: 5px; }
</style>
</head>
<body>
<div id="container">
<div id="someid1" class="style" style="background-color:green;">div1</div>
<div id="someid2" class="style" style="background-color:blue;">div2</div>
<div id="someid3" class="style" style="background-color:red;">div3</div>
<div id="someid4" class="style" style="background-color:purple;">div4</div>
</div>
<p>Pass in a comma separated list of numbers 1-4 to reorder divs</p>
<input id="order" type="text" />
<input id="go" type="button" value="Re-Order" />
</body>
</html>
[Edit], This is tested and works:
var order = [3,2,1,4];
var container = $("#container");
var children = container.children();
container.empty();
for (var i = 0; i < order.length; i++){
container.append(children[order[i]-1])
}
The i-1 is necessary since your ordering starts at 1 but arrays are indexed from 0.
Thanks to J-P and Russ Cam for making me look at it again.
Here's a jQuery-less solution:
function appendNodesById(parent, ids) {
for(var i = 0, len = ids.length; i < len; ++i)
parent.appendChild(document.getElementById(ids[i]));
}
appendNodesById(document.getElementById('container'),
['someid4', 'someid2', 'someid3', 'someid1']);
If you have all the content in the array then remove all the content from the wrapper div container in your code. then start adding the received divs one by one:
var v = $(ar[0]).append(ar[1]).append(ar[2]);
$("#container").html(v);
If this does not works then look into this thread that discusses about positioning elements relative to other elements.