When I click the show/hide buttons above the tag, it works fine. But for the button below that (front full) it won't hide the contents of the tag. If i move the button above the beginning tag it will work. How can I get it to link to the hide tag above it?
<input type="button" onclick="show(this);" value="show"/>
<input type="button" onclick="hide(this);" value="hide"/>
<hide class="inner" style="display.none;">
<input type="button" onclick="showSpoiler1(this);" value="Front flip variaion (ramp)" />
<input type="button" onclick="showSpoiler2(this);" value="Front flip variaion (flat)" />
<input type="button" onclick="showSpoiler3(this);" value="Backflip variations" />
<input type="button" onclick="showSpoiler4(this);" value="Sideflip Variations (ramp)" />
<input type="button" onclick="showSpoiler5(this);" value="Twists and other tricks" />
<div1 class="inner" style="display:none;">
<ul>
<input type="button" onclick="hide(this);" value="Front full"/>
<li>Double Front</li>
<li>Aerial Twist</li>
</ul>
</div1>
</hide>
JS
function show(obj)
{
var inner = obj.parentNode.getElementsByTagName("hide")[0];
inner.style.display = "";
}
function hide(obj)
{
var inner = obj.parentNode.getElementsByTagName("hide")[0];
inner.style.display = "none";
}
In the buttons below, you have:
<div1 class="inner" style="display:none;">
<ul>
<input type="button" onclick="hide(this);" value="Front full"/>
<li>Double Front</li>
then in the function:
function show(obj)
{
var inner = obj.parentNode.getElementsByTagName("hide")[0];
So when you click on the button, obj is a reference to the button. It's parent node will be an LI that will be inserted by error correction (since a button can't be a child of a UL element). That parent node doesn't have a child "hide" element.
Even if the button was a child of the UL, it doesn't have any hide children either. Consider instead:
var inner = document.getElementsByTagName("hide")[0];
you can't you the same hide() function for both buttons , as what it does is pretty specific as to what it hides. why don't you try something like :
function hideParent(obj)
{
var inner = obj.parentNode.parentNode;
inner.style.display = "none";
}
<hide class="inner" style="display.none;">
...
<div1 class="inner" style="display:none;"> // <- assuming this is what this supposed to nbe hidden
<ul>
<input type="button" onclick="hideParent(this);" value="Front full"/>
<li>Double Front</li>
<li>Aerial Twist</li>
</ul>
</div1>
</hide>
if all the buttons are supposed to hide the same thing - the <hide> element then do something like this:
function hide(obj)
{
var inner = getElementbyTagName("body").getElementsByTagName("hide")[0];
inner.style.display = "none";
}
like the other comments suggest , don't use <hide> just use <div> then use getElementById
Your hide and show functions look for hide tags that are children of the button's parent, i.e. siblings of the button. The parent of the button is the div, and the parent of the div is the hide.
If you want the button to affect the hide element that contains it, try hide(this.parentNode.parentNode)
Or, you could change the hide/show functions to iterate progressively higher in the DOM hierarchy until it finds a hide element, so it will work for siblings, parents, and siblings of parents. Perhaps something like:
function hide(obj)
{
if (obj !== document)
{
var parent = obj.parentNode;
var inner = parent.getElementsByTagName("hide");
if (inner.length > 0)
{
inner[0].style.disply = "none";
} else {
hide(parent)
}
}
}
Related
Apologies in advance, I'm not terribly familiar with Javascript, but I do understand what this code is doing and why it is causing me this problem. I'm just not sure how to go about solving it AT all.
On my webpage I have an open/close dialogue toggle which is the parent div, the dialogue box is hidden upon the page loading. Within this dialogue box are more hidden divs for the dialogue options. Problem is, when one of the dialogue options is clicked, the script hides the entire dialogue box, preventing any of the dialogue options from being seen, because it can only show one div at a time, regardless of its parent or child status. When a div is clicked, all other divs are re-hidden.
I need the parent div to remain visible until the dialogue box toggle is clicked again. The individual choices DO need to hide/unhide when another choice is clicked.
Not sure if I should include any CSS here, it's just styling the dialogue box and its buttons within.
<div id="dialogue" style="display:none;">
<div class="room">
Room description here. What do you do?
<div class="buttons">
Pet the cat.
<br>
<div id="cat" style="display:none;">aw yeah kitty time</div>
Turn on the radio.
<br>
<div id="radio" style="display:none;">
<br>
audio file and tracklist here
</div>
</div>
</div>
</div>
<span class="toggle">
[Open/close dialogue.]
</span>
<script type="text/javascript">
var divs = ["cat", "radio", "dialogue"];
var visibleDivId = null;
function divVisibility(divId) {
if(visibleDivId === divId) {
visibleDivId = null;
} else {
visibleDivId = divId;
}
hideNonVisibleDivs();
}
function hideNonVisibleDivs() {
var i, divId, div;
for(i = 0; i < divs.length; i++) {
divId = divs[i];
div = document.getElementById(divId);
if(visibleDivId === divId) {
div.style.display = "block";
} else {
div.style.display = "none";
}
}
}
</script>
I probably need a third function here because currently all the toggles are grouped together, hence why they're interacting like this, but I don't have the first clue how to accomplish this. I have been looking and haven't found anything that seems to match my needs.
Made a few corrections to your HTML so the href does not refresh the page on click. Also added in a few attributes (aria-controls) to track which div the button controls. I added comments to the JavaScript. There are plenty of Aria attributes they typically help with accessibility but they are super useful for keeping track of things in HTML and passing information to JavaScript.
//create a function to handle the click that takes in the event as a argument
function handleClick(event) {
//find out which div the button controls
const ariaControls = event.currentTarget.getAttribute("aria-controls"),
//select the controlled div
controlledAria = document.getElementById(ariaControls);
// if the controlled div is cat
if (ariaControls === "cat") {
// hide the radio div
document.getElementById("radio").classList.add("hide");
// if the controlled div is radio
} else if (ariaControls === "radio") {
// hide the car div
document.getElementById("cat").classList.add("hide");
}
//toggle the hide div on the controlled div
controlledAria.classList.toggle("hide");
}
//select all the buttons
const buttons = document.querySelectorAll("button");
//for each button add an event listener when the button is clicked run the handle click function
buttons.forEach(button => button.addEventListener("click", handleClick))
.hide {
display: none;
}
<div id="dialogue" class="hide">
<div class="room">
Room description here. What do you do?
<div class="buttons">
<button aria-controls="cat">Pet the cat.</button><br>
<div id="cat" class="hide">aw yeah kitty time</div>
<button aria-controls="radio">Turn on the radio.</button><br>
<div id="radio" class="hide">audio file and tracklist here
</div>
</div>
</div>
</div>
<span class="toggle"><button aria-controls="dialogue">[Open/close dialogue.]</button></span>
I want to make a page to upload the avatar.
By default, I use the vector to show where the image will appear
and then provide a button to upload the URL to change the avatar.
That's all it is! But the script still doesn't work.
Pleased to hear your feedback on how to fix it. Bless
<img id="put_image_here_bitch" src="https://cdn0.iconfinder.com/data/icons/finance-vol-2-4/48/77-512.png" alt="" width="100px" height="100px">
<div class="block" > The person who uploads this is cool
</div>
<button onclick="hideElement()">Click to upload photo by URL</button>
<div>
<input id="input" autofocus class='hidden_element' style="display: none;" type="text" id="input">
</div>
<div>
<button class='hidden_element' style="display: none;" onclick="uploadImage()">UPLOAD</button>
</div>
This is my script
function hideElement(){
var hide = document.getElementsByClassName('hidden_element');
if (hide.style.display === "none") {
hide.style.display = "block";
} else {
hide.style.display = "none";
}
}
var uploadImage = function(){
image = document.getElementById('input').value;
showImage = document.getElementById('put_image_here_bitch').setAttribute('src', image);
};
As stated in the comments, .getElementsByClassName() returns a collection of elements, not a single element and your code attempts to call the style property of the collection, which doesn't exist.
Instead, you need to loop through the collection and operate on the elements within the collection individually, but don't use .getElementsByClassName() and instead use .querySelectorAll().
var hidden = document.querySelectorAll('.hidden_element');
function hideElement(){
// Loop over the colleciton elements
hidden.forEach(function(element){
if (element.style.display === "none") {
element.style.display = "block";
} else {
element.style.display = "none";
}
});
}
var uploadImage = function(){
image = document.getElementById('input').value;
showImage = document.getElementById('put_image_here_bitch').setAttribute('src', image);
};
<img id="put_image_here_bitch" src="https://cdn0.iconfinder.com/data/icons/finance-vol-2-4/48/77-512.png" alt="" width="100px" height="100px">
<div class="block" > The person who uploads this is cool
</div>
<button onclick="hideElement()">Click to upload photo by URL</button>
<div>
<input id="input" autofocus class='hidden_element' style="display: none;" type="text" id="input">
</div>
<div>
<button class='hidden_element' style="display: none;" onclick="uploadImage()">UPLOAD</button>
</div>
But, beyond that, you should also avoid using inline styles as they are the most specific way of setting a style and therefore the hardest to override. They also often require duplicated code to be written. Instead, use CSS classes as shown below:
// Get references to the DOM elements that you'll need to work with
const btnUpload = document.querySelector("button"); // find the first button
const hidden = document.querySelectorAll(".hidden");
const upload = document.querySelector(".upload");
// Do your event binding in JavaScript, not in HTML
btnUpload.addEventListener("click", hideElement);
upload.addEventListener("click", uploadImage);
function hideElement(){
// Loop over the collection of hidden elements
hidden.forEach(function(item){
// See how much more simple it is to work with classes?
item.classList.toggle("hidden");
});
}
function uploadImage(){
showImage = document.getElementById('put_image_here_bitch').setAttribute('src', input.value);
};
.hidden { display:none; }
<img id="put_image_here_bitch" src="https://cdn0.iconfinder.com/data/icons/finance-vol-2-4/48/77-512.png" alt="" width="100px" height="100px">
<div class="block" > The person who uploads this is cool
</div>
<button>Click to upload photo by URL</button>
<div>
<input id="input" autofocus class='hidden' type="text" id="input">
</div>
<div>
<button class='hidden upload'>UPLOAD</button>
</div>
simple.. getElementsByClassName() returns an HTMLCollection with all DOM elements containing that class. An HTMLCollection is like an array ( but not really ) containing element references.
thus you need to define which entry in the array you want to handle ( even if there's only one )
your code should work by simply adding [0] to your DOM read ( the '0' means the first element in the collection )
ex:
var hide = document.getElementsByClassName('hidden_element')[0];
I want to receive information from my users using buttons, so i created two sections for that. When the user clicks a button i need it to change its style so the user will see a difference in what they clicked even when they click another button in the other section. The problem is that when another button is clicked the other section looses its style (so the user wouldn't know what he previously clicked). I pasted the html code of the part with the problem(below)
<div>
<p><strong>Network</strong></p>
<button class="btn25" onclick = "gfg_Run()">
MTN
</button>
<button class="btn25">
Glo
</button>
<button class="btn25">
9 Mobile
</button>
<button class="btn25">
Airtel
</button>
</div>
<div>
<p><strong>Data</strong></p>
<button class="btn25">
1Gb
</button>
<button class="btn25">
2Gb
</button>
<button class="btn25">
3Gb
</button>
<button class="btn25">
5Gb
</button>
<button class="btn25">
10Gb
</button>
</div>
i need it to change its style so the user will see a difference
Create a class called active and then set the buttons to use that class when the user selects them.
The problem is that when another button is clicked the other section looses its style
The issue is that you need to separate out each group. I added a class on the parent div that distinguishes the two groups: "data" or "speed". The button background will change to blue for each of the groups because it is limited to that class in the query as ".data" does here:
document.querySelectorAll('.data .btn25');
Here is an example snippet showing the basic technique.
const dataChoices = document.querySelectorAll('.data .btn25');
dataChoices.forEach(function(choice) {
choice.addEventListener('click', function() {
removeClass(dataChoices);
this.classList.add('active');
});
});
const speedChoices = document.querySelectorAll('.speed .btn25');
speedChoices.forEach(function(choice) {
choice.addEventListener('click', function() {
removeClass(speedChoices);
this.classList.add('active');
});
});
function removeClass(dataGroup) {
dataGroup.forEach(function(choice) {
choice.classList.remove('active');
});
}
.active {
background-color: lightblue;
}
div {
float: left;
margin-right: 10px;
}
<div class="data">
<p><strong>Data</strong></p>
<button id="1" class="btn25">1Gb</button>
<button id="2" class="btn25">2Gb</button>
<button id="3" class="btn25">3Gb</button>
<button id="4" class="btn25">5Gb</button>
<button id="5" class="btn25">10Gb</button>
</div>
<div class="speed">
<p><strong>Speed</strong></p>
<button id="1" class="btn25">1Mbps</button>
<button id="2" class="btn25">2Mbps</button>
<button id="3" class="btn25">3Mbps</button>
<button id="4" class="btn25">5Mbps</button>
<button id="5" class="btn25">10Mbps</button>
</div>
I have an alternate way to do this, but honestly Christopher Taleck's answer is perfectly fine as well, I just had a different conception of how to solve the problem and figured that would be informative for others facing a similar situation.
Here is a link to a JSFiddle with a working example: JSFiddle
Here is the complete code, an explanation of everything is provided below:
index.html
<!DOCTYPE html>
<html>
<head>
<title>Button Practice</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="network-container">
<h2>Network</h2>
<button type="button" class="btn25">MTN</button>
<button type="button" class="btn25">Glo</button>
<button type="button" class="btn25">9 Mobile</button>
<button type="button" class="btn25">Airtel</button>
</div>
<div class="speed-container">
<h2>Speed</h2>
<button type="button" class="btn25">1Mbps</button>
<button type="button" class="btn25">2Mbps</button>
<button type="button" class="btn25">3Mbps</button>
<button type="button" class="btn25">5Mbps</button>
<button type="button" class="btn25">10Mbps</button>
</div>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
script.js
function ButtonContainer(element) {
this.element = element;
this.selectedButton = null;
this.element.addEventListener('click', function(e) {
if (e.target.type !== 'button') return;
if (this.selectedButton) this.selectedButton.classList.remove('active');
e.target.classList.add('active');
this.selectedButton = e.target;
});
}
const networkContainer = new ButtonContainer(document.querySelector('.network-container'));
const speedContainer = new ButtonContainer(document.querySelector('.speed-container'))
style.css
.active {
background-color: #9AD58E;
}
GENERAL CODE EXPLANATION
First, I would suggest removing this call to gfg_Run() from the .html file:
// index.html
<button class="btn25" onclick = "gfg_Run()">
And creating a separate file to hold your JS. Then, you can just link that file like so, right before the closing <body> tag (I created a file called script.js for this purpose):
// index.html
<script type="text/javascript" src="script.js"></script>
Also, I would suggest adding a descriptive class based on the type of buttons that each <div> holds, like so:
// index.html
<div class="network-container">
------ rest of the container ------
<div class="speed-container">
------ rest of the container ------
To handle the change of style on the button click, you can create a class that is added to the clicked button. I will use .active as that is a pretty common convention. This can be in a style.css file that you link from the head of the HTML document:
// style.css
.active {
background-color: #9AD58E;
}
In that script.js file, my approach would be to create a constructor function that represents the container that holds the buttons:
// script.js
function ButtonContainer(element) {
this.element = element;
this.selectedButton = null;
this.element.addEventListener('click', function(e) {
if (e.target.type !== 'button') return;
if (this.selectedButton) this.selectedButton.classList.remove('active');
e.target.classList.add('active');
this.selectedButton = e.target;
});
}
ButtonContainer CONSTRUCTOR FUNCTION EXPLANATION
We set the ButtonContainer up with two properties, element and selectedButton. element represents the DOM element which contains the buttons (in our case, a <div>), and the selectedButton represents the last <button> in this <div> that was clicked.
The eventListener will fire whenever the <div> is clicked anywhere inside it's borders. We only want to handle clicks on a <button> element within this <div>, so we exit the listener if the target of the event was not a button with the line below:
if (e.target.type !== 'button') return;
Next, we want to see of there is already a selectedButton in this <div>, and if so, we need to remove the .active class from it so we don't have two buttons in the same <div> that have colored backgrounds:
if (this.selectedButton) this.selectedButton.classList.remove('active')
Finally, we set the .active class on the <button> that triggered the event, and set the selectedButton property of this ButtonContainer object to that button, so that we can keep track of the currently selected button in this <div>:
e.target.classList.add('active');
this.selectedButton = e.target;
Then simply create two instances of the ButtonContainer that represent the <div> elements containing each group of buttons (each <div> should have a class representing the type of buttons it holds):
const networkContainer = new ButtonContainer(document.querySelector('.network-container'));
const speedContainer = new ButtonContainer(document.querySelector('.speed-container'));
In this way, you don't have to loop through the buttons and add event listeners to each, you simply allow each container to handle it's own button events. I like this approach because we seem to be treating the buttons like two separate groups, and this logically mirrors that.
I have a minimize button which sets the visibility of the selection to 'hidden'.
I also have a maximize button which sets the visibility of the selection to 'visible'.
Now I want to do this with only one button, but be able to pass through different selections to the function this button calls.
I can easily do this if I was only using the function on one object. For example, I would create a variable, say, hidden=false. Once I click the button it would check what hidden is equal to. If it is true then it would show the selection then change it to false. If it is false, it would hide the selection and change it to true.
Now I want to do this so I can pass objects through to one function and for that function to then check what the hidden value for that selection is.
I want to make a function as I want to be able to re-use this code, rather than having to do alot of if statements everytime I want to show/hide something.
What I have already.
//Pseudo code of buttons
MinimizeButton = onClick(hideThis(selection1));
MaximizeButton = onClick(ShowThis(selection1));
function hideThis(selection){
selection.classed("hidden", true);
}
function showThis(selection){
selection.classed("hidden", false);
}
.hidden{
visibility:hidden;
}
I want to be able to do this with one button rather than two as I have at the moment
I would use a class on the buttons (button.toggle) and a data attribute holding the selector of elements to modify.
function toggleBySelector() {
var target = $(this).data("selector");
$(target).toggle();
}
$("button.toggle").on("click", toggleBySelector);
.red {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div>
<button class="toggle" data-selector="#single">Toggle Single</button>
</div>
<div>
<button class="toggle" data-selector=".red">Toggle Red</button>
</div>
</div>
<div>
<div class="red">Red Div</div>
<div id="single">Single Div</div>
<div class="red">Additional Red Div</div>
</div>
This binds the template to itself (or potentially other templates), but allows you to keep your DOM and script largely separate. Each button knows what elements it should toggle, using a jQuery selector in the data-selector attribute.
Using jQuery's toggle method, you don't need to keep track of which elements are shown or hidden, it will handle that for you.
A general approach:
function generalHideOrShow(element)
{
if (element instanceof Element)
{
//single element passed
element = [element]; //mimic node list
}
if(element.length && element.length > 0 && element[0] instanceof Element)
{
//node list
for (var i = 0; i < element.length; ++i)
{
if (element[i].getAttribute("data-hidden") == "true" )
{
$(element[i]).removeClass("hidden");
element[i].setAttribute("data-hidden", false);
}
else
{
element[i].setAttribute("data-hidden", true);
$(element[i]).addClass("hidden");
}
}
}
else
{
return false;
}
}
$("#button1").click(function(){
generalHideOrShow($("div"));
});
$("#button2").click(function(){
generalHideOrShow($("span"));
});
$("#button3").click(function(){
generalHideOrShow(document.body.querySelectorAll("span:nth-child(odd)"));
});
.hidden{
visibility: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div>test div 1</div>
<div>test div 2</div>
<div>test div 3</div>
<div>test div 4</div>
<span>test span 1</span>
<span>test span 2</span>
<span>test span 3</span>
<span>test span 4</span>
<br />
<button id="button1">Click to toggle the divs</button>
<button id="button2">Click to toggle the spans</button>
<button id="button3">Click to toggle the even spans</button>
To use:
generalHideOrShow( $("divs") );
That will give an array of elements to the function.
This function will work with an element (passed with document.getElementById("element") for example), a Node list (passed with document.querySelectorAll("div") for example) and jQuery selectors.
I'm pulling a content from PHP array and I have a situation like this:
<div class="weight-display">
<span>04/25/2011</span> <span>100lbs</span> <span>Edit</span> <a href="http://foo.com">Delete</span>
</div>
<div class="weight-display">
<span>04/27/2011</span> <span>150lbs</span> <span>Edit</span> <a href="http://foo.com">Delete</span>
</div>
etc...
Now when somebody clicks on Edit within, let's say, first div where weight is 100lbs, I just need that "div" to change and to have input field instead of simple text where weight is (while others will remain the same) and to be like this:
<div class="weight-display">
<span>04/25/2011</span> <input type="text" value="100" /> <span>Save</span> <span>Cancel</span>
</div>
<div class="weight-display">
<span>04/27/2011</span> <span>150lbs</span> <span>Edit</span> <a href="http://foo.com">Delete</span>
</div>
etc..
So basically div has to "reload itself" and change content. Now I really need some very simple Javascript solution. Preferably I would like a solution with a hidden div beneath original one, so they just swap places when user clicks on EDIT and in a case if CANCEL is pressed to swap places again so original div with text is displayed...
Thanks,
Peter
<style type="text/css">
/* Normal mode */
.weight-display div.edit {display:none}
/* Editor mode */
.weight-edit div.show {display:none}
</style>
<div class="weight-display">
<button onclick="toggle(this)">Edit this!</button>
<div class="edit"><input type="text" value="Test" /></div>
<div class="show">Test</div>
</div>
<script type="text/javascript">
function toggle(button)
{
// Change the button caption
button.innerHTML = button.innerHTML=='Edit this!' ? 'Cancel' : 'Edit this!';
// Change the parent div's CSS class
var div = button.parentNode;
div.className = div.className=='weight-display' ? 'weight-edit' : 'weight-display';
}
</script>
What you suggest is basically correct. I would generate two div's one for display and one edit. The edit div will initially have display: none. When the Edit is clicked, hide the display div and show the edit div.
How about something like:
onClick event calls a function (EDITED to be a little smarter than my original brute force method):
function swapdivs ( id_of_topdiv, id_of_bottomdiv ) {
var topdiv = getRefToDiv( id_of_topdiv );
var bottomdiv = getRefToDiv( id_of_bottomdiv );
var temp = topdiv.style.zIndex;
topdiv = bottomdiv.style.zIndex;
bottomdiv = temp.style.zIndex;
}
Could that or similar work for you? Or am I missing some subtle yet crucial requirement?