I am trying to implement this site to imitate a survey creation application. In this website you can add questions and then add some options to each question and edit the text in both Question Titles and Options.
But user should be able to remove an option as well. I have implemented the addition of the option but I don't know how to let the user delete a specific option. After this I can imagine it will be the same to do for deletion of questions.
I have done so each option has it's own remove option button but I just don't know how I should actually delete the current option.
If someone has done or knows how this problem should be approached I would appreciate the help.
This is my code:
const questionnaire = document.getElementById('questionaire');
newQuestion();
function newQuestion() {
questionnaire.insertAdjacentHTML('beforeend',
`<div class="question"> <div contenteditable="true">Your Question</div>
<ul> </ul>
<button class="addButton" type="button">Add Option</button>
</div>`);
newOption(questionnaire.querySelector("div.question:last-child ul"));
}
function newOption(q) {
q.insertAdjacentHTML('beforeend',
`<li class="optionName">
<span contenteditable="true">Option</span>
<input type="checkbox">
<button class="removeButton" type="button">Remove Option</button>
</li>`);
}
questionnaire.onclick = ev => {
if (ev.target.tagName === "BUTTON") {
if (ev.target.className === "addButton") {
newOption(ev.target.closest(".question").querySelector('ul'))
}
else if (ev.target.className === "removeButton") {
/* HERE I DON'T KNOW WHAT TO WRITE */
}
}
}
document.getElementById("addQuButton").onclick = newQuestion
body {
background-color: #00ffaa;
}
#myText {
margin-top: 15px;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
text-align: center;
}
.question {
border: 3px solid black;
border-radius: 25px;
margin: 30px 200px 20px 200px;
text-align: center;
}
.question ul li {
display: block;
}
<h1 id="myText" contenteditable="true">Survey Name</h1>
<button type="button" id="addQuButton">Add Question</button>
<form>
<div id="questionaire"></div>
</form>
To remove an option just add any element with a specific class like I am adding the span tag with the class name of remove-li in your option code.
and add the condition in the click event, which you've already set to the main container to check the event's clicked target, and only remove when the event's target has the given class which in our case is remove-li.
/* Getting the questionaire container*/
const questionnaire = document.getElementById('questionaire');
/*call the 'newQuestion' to add a question with options in container */
newQuestion();
/*define the 'newQuestion' method to insert the new question html in the container at the last position */
function newQuestion() {
questionnaire.insertAdjacentHTML('beforeend',
`<div class="question">
<div contenteditable="true">Your Question</div>
<ul></ul>
<button type="button">Add Option</button>
</div>`);
/*calling the 'newOption' method where adding the option into the last child of question's unorderd list */
newOption(questionnaire.querySelector("div.question:last-child ul"));
}
/*defining the newOption method where we insert the option html into provided selector as q */
function newOption(q) {
//Add the span with a class of 'remove-li'
q.insertAdjacentHTML('beforeend',
`<li class="optionName">
<span contenteditable="true">Option</span>
<input type="checkbox"><span class="remove-li">X<span>
</li>`);
}
//set the onclick listener on main container
questionnaire.onclick = ev => {
// check the target with tag is button to add the new option only when the click on button
if (ev.target.tagName === "BUTTON") {
newOption(ev.target.closest(".question").querySelector('ul'))
}else if(ev.target.className == 'remove-li'){ //Check the clicked target is remove button for the option to remove it
ev.target.parentNode.remove();
}
}
//set the onclick event on add question button
document.getElementById("addQuButton").onclick = newQuestion
body {
background-color: #00ffaa;
}
#myText {
margin-top: 15px;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
text-align: center;
}
.question {
border: 3px solid black;
border-radius: 25px;
margin: 30px 200px 20px 200px;
text-align: center;
}
.question ul li {
display: block;
}
/*Add some style for the remove button*/
.remove-li{
padding: 5px;
background-color:red;
color:white;
cursor: default;
}
<h1 id="myText" contenteditable="true">Survey Name</h1>
<button type="button" id="addQuButton">Add Question</button>
<form>
<div id="questionaire"></div>
</form>
Please check the added code.
May it helps to learn :)
Related
I am having this problem, I created a button, and inside I have a for Icon, and inside the element I have span to style the text next to the Icon (the Icon from is humberger from awesome font)
the issue is:
in javascript, I created an onclick function for the button element using the ID btnm, but when I click on the text or the icon in the button does work though when I click around the text and the icon in the button the onclick works fine.
I cannot understand why the icon and text are in the button.
please help
document.addEventListener('DOMContentLoaded', function(){
var menubtn = document.getElementById('btnm');
var mobilemenu = document.getElementById('navigation-mobile');
// When the user clicks on the button, open the modal
menubtn.onclick = function() {
if (mobilemenu.style.display == 'block') {
mobilemenu.style.display = "none";
}
else {
mobilemenu.style.display = 'block';
}
}
}
.mobile-menu-btn {
float: right;
display: block;
padding: 3px 3px 0px 0px;
}
.humberger {
background-color: $identity-color;
font-size: 20px;
border: $identity-color;
border: none;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
.menu-pargraph {
font-size: 14px;
vertical-align: middle;
padding-left: 5px;
margin: 0;
width: 100%;
}
<div class ="mobile-menu-btn">
<button class="humberger" id="btnm">
<i class="menu-btn fas fa-bars">
<span class="menu-pargraph">Menu</span>
</i>
</button>
</div>
<div id="navigation-mobile">
<ul>
<li>home</li>
<li>video</li>
<li>contact</li>
</ul>
</div>
Explanations in comments below. You had the id (and click listener) on the div, not the button and your 'Menu' text was probably looking funky b/c it was inside the icon element, inheriting the icon font family.
<div class ="mobile-menu-btn">
<button class="humberger" id="btnm"> <!-- put the id here -->
<i class="menu-btn fas fa-bars"></i>
<span class="menu-pargraph">Menu</span> <!-- move outside of the fontawesome icon -->
</button>
</div>
Also you can make your life easier with the show/hide using a class
css:
#navigation-mobile{
display:none;
/* and whatever other styles you have here */
}
.show {
display:block;
}
then in your script:
menubtn.onclick = function() {
mobilemenu.classList.toggle('show');
}
You missed a ")" in your JS code.
document.addEventListener('DOMContentLoaded', function(){
var menubtn = document.getElementById('btnm');
var mobilemenu = document.getElementById('navigation-mobile');
// When the user clicks on the button, open the modal
console.log("ok")
}
) // Here you have to add parenthesis
I want to set up a JS based tab navigation but somehow the Tab content doesnt show properly.
The JS code seems off but I cannot find the error.
I just want the Tab Content to show that belongs to one tab.
The Code looks like this:
function openPage(e, pageName) {
// Declare all variables
let i, content, tabLinks;
// Get all elements with class="tabs_content" and hide them
content = document.getElementsByClassName("tabs_content");
for (i = 0; i < content.length; i++) {
content[i].style.display = "none";
}
// Get all elements with class="tabs_button" and remove the class "active"
tabLinks = document.getElementsByClassName("tabs_button");
for (i = 0; i < tabLinks.length; i++) {
tabLinks[i].className = tabLinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the link that opened the tab
document.getElementById(pageName).style.display = "block";
e.currentTarget.className += " active";
}
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
.wrapper{
display: grid;
grid-template-columns: 150px 1fr;
border:1px solid #cccccc;
padding: 15px;
margin: 0 auto;
grid-template-areas:
'nav main '
'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
flex-shrink: 0;
min-height: 400px;
}
.tabs_content {
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
}
.tabs_button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_button:active {
background: #dddddd;
}
.tabs_button:not(:last-of-type){
border-bottom: 1px solid #cccccc;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button class="tabs_button" onclick="openPage(e, 'Home')" id="defaultOpen">Home</button>
<button class="tabs_button" onclick="openPage(e, 'News')">News</button>
<button class="tabs_button" onclick="openPage(e, 'Contact')">Contact</button>
</nav>
<section class="tabs_content tabs_content--active">
<div id="Home" class="tabs_content">
<h2>Tab #1</h2>
<p>Content Page 1 </p>
</div>
<div class="tabs_content">
<h2>Tab #2</h2>
<p>Content Page 2 </p>
</div>
<div class="tabs_content">
<h2>Tab #3</h2>
<p>Content Page 3</p>
</div>
</section>
</div>
I cannot find the error, but the problem seems to be the <section>. Any help is much appreciated.
Your code has more than one problem.
The one you're currently stuck on is that e is not defined in this template bit:
<button class="tabs_button" onclick="openPage(e, 'Home')" id="defaultOpen">Home</button>
The default name of the event when you want to pass it to the function is event, not e (and you can't change it, it's a convention - you can only change its name in the function). In other words, even if in the function it's named e, you still have to reference it as event in the template.
Here are the rest of the problems, in the order I encountered them as I worked my way into making it work:
the .tabs_button:active selector is wrong. You probably want to use .tabs_button.active (since you're applying the class active to it).
you have tabs_content class on the tabs wrapper, so the entire wrapper is hidden, when you probably only want to set display:none onto its children.
you're missing the ids on second and third tab
you need width: 100% on .tabs_content
See it here:
function openPage(e, pageName) {
let i, content, tabLinks;
content = document.getElementsByClassName("tabs_content");
for (i = 0; i < content.length; i++) {
content[i].style.display = "none";
}
tabLinks = document.getElementsByClassName("tabs_button");
for (i = 0; i < tabLinks.length; i++) {
tabLinks[i].className = tabLinks[i].className.replace(" active", "");
}
document.getElementById(pageName).style.display = "block";
e.currentTarget.className += " active";
}
document.getElementById("defaultOpen").click();
.wrapper {
display: grid;
grid-template-columns: 150px 1fr;
border: 1px solid #cccccc;
padding: 15px;
margin: 0 auto;
grid-template-areas: 'nav main ' 'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
flex-shrink: 0;
min-height: 400px;
}
.tabs_content {
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
width: 100%;
}
.tabs_button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_button.active {
background: #dddddd;
}
.tabs_button:not(:last-of-type) {
border-bottom: 1px solid #cccccc;
}
body {
margin: 0;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button class="tabs_button" onclick="openPage(event, 'Home')" id="defaultOpen">Home</button>
<button class="tabs_button" onclick="openPage(event, 'News')">News</button>
<button class="tabs_button" onclick="openPage(event, 'Contact')">Contact</button>
</nav>
<section class="tabs_content--active">
<div id="Home" class="tabs_content">
<h2>Tab #1</h2>
<p>Home</p>
</div>
<div id="News" class="tabs_content">
<h2>Tab #2</h2>
<p>News</p>
</div>
<div id="Contact" class="tabs_content">
<h2>Tab #3</h2>
<p>Contact</p>
</div>
</section>
</div>
Apart from that, in my estimation, you're doing too much by JavaScript. Instead of applying inline styles, you should only apply and remove classes which, in turn, apply styles. You end up writing less code and it's easier to manage & debug.
Another thing I would personally change is replace className usage with classList, which has a more powerful and more expressive syntax.
This doesn't mean it's not doable the way you set out to do it.
The main problem when mixing stylesheet CSS with inline styles is that inline styles can only be overwritten with !important, which leads to a whole new level of problems. By only applying classes you keep the specificity battle inside your stylesheet, which is where it should take place.
Here's how I'd write it:
function openPage(e) {
const target = e.target.dataset['target'];
if (target) {
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
el.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
document.getElementById(el.dataset['target'])
.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
})
}
}
document.querySelector('.tabs_sidebar').addEventListener('click', openPage);
.wrapper {
min-height: 100vh;
display: grid;
grid-template-columns: 150px 1fr;
border: 1px solid #cccccc;
padding: 15px;
grid-template-areas: 'nav main'
}
.tabs_sidebar {
grid-area: nav;
display: flex;
flex-direction: column;
background: #cccccc;
}
section {
display: flex;
flex-direction: row;
}
.tabs_content--active > div {
width: 100%;
grid-area: main;
background: #f6e3e3;
padding-left: 15px;
font-size: 1rem;
display: none;
}
.tabs_content--active > div.active {
display: block;
}
.tabs_sidebar > button {
display: block;
padding: 10px;
background: #eeeeee;
border: none;
width: 100%;
outline: none;
cursor: pointer;
font-size: 1rem;
}
.tabs_sidebar > button.active {
background: #dddddd;
}
.tabs_sidebar > button:not(:last-of-type) {
border-bottom: 1px solid #cccccc;
}
body {
margin: 0;
}
* {
box-sizing: border-box;
}
<div class="wrapper">
<nav class="tabs_sidebar">
<button data-target="Home" class="active">Home</button>
<button data-target="News">News</button>
<button data-target="Contact">Contact</button>
</nav>
<section class="tabs_content--active">
<div id="Home" class="active">
<h2>Tab #1</h2>
<p>Home</p>
</div>
<div id="News">
<h2>Tab #2</h2>
<p>News</p>
</div>
<div id="Contact">
<h2>Tab #3</h2>
<p>Contact</p>
</div>
</section>
</div>
Note I've also made changes to markup (HTML) and CSS, not just JavaScript.
As requested in comments, here's an explanation on what the JavaScript code does:
Instead of placing a click handler on each individual button, I've placed one on their parent. It has the advantage of working on future buttons as well, should your page be dynamic. If you bind on each button, once the buttons change you have to figure out which ones are new and only bind the handler on those (you don't want to bind the handler twice on the same element).
When a click is performed inside that button container, you first have to determine which button was clicked (or if the click was outside of any existing buttons). See how data attributes work here. If the click was performed on a button (if (target)), instead of having two loops (one through the buttons and one through the tabs), I only used one loop (through the buttons).
Instead of doing a classic for loop I chose to cast the NodeList returned by querySelectorAll to an array which I can then iterate using .forEach().
Note: To be totally fair, I suspect the for loop (what you used) is more performant but the difference is quite small and I tend to go for the shorter syntax:
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
// do stuff with each looped element. In this case, a button
})
Now that we have the element, let's do something with it: Add or remove the class active based on whether or not the current button's target matches the one that was clicked (which we already have saved in target const).
I personally prefer classList to className. It works on the classes array, rather than on the resulting classes string. Has add, remove and replace methods and you don't end up having to worry about adding spaces manually (or trimming them). I'm using
el.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
instead of
if (el.dataset['target'] === target) {
el.classList.add('active');
} else {
el.classList.remove('active');
}
I used the array notation to call add and remove methods on classList. (i.e: in Javascript you can write foo.bar(arg) as foo['bar'](arg) - all methods are still object properties). The advantage is that inside the array notation you can switch the method being called, based on a ternary condition, which is exactly what I'm doing above.
Using the same technique, I'm also adding/removing the active class from the corresponding tab, which I'm selecting by id.
document.getElementById(el.dataset['target'])
.classList[el.dataset['target'] === target ? 'add' : 'remove']('active');
The only thing that could slightly be improved about my code is saving the result of the ternary condition instead of doing it twice. Like this:
if (target) {
[...document.querySelectorAll('.tabs_sidebar > button')].forEach(el => {
const isActive = el.dataset['target'] === target;
el.classList[isActive ? 'add' : 'remove']('active');
document.getElementById(el.dataset['target'])
.classList[isActive ? 'add' : 'remove']('active');
})
}
Hope that makes sense.
There are 5 boxes, which can be changed from 'white'<->'yellow' colors by mouse events (mouseover, mouseout and click). There is also a blue area with text displaying the level of the clicked box.
After clicking into the third box, I got 'hard level' text in blue area and 3 boxes color in yellow.
What I need is to return it to the default level ('easy level' and first box in yellow only) by clicking the reset button.
I have been trying do this like this , but it isn't working:
resetBtn = document.querySelector('#update');
and eventlistener:
resetBtn.addEventListener('click', highlightStars(`#star1`), true)
Here is an example:
window.addEventListener('DOMContentLoaded', changeStars, false);
const resetBtn = document.querySelector('#update');
/* Change level of the game depending on user choice */
function changeStars() {
/* Displaying level text inside blue box */
const updateAltText = currentLevelIndex => {
let levelText = document.querySelector('#level-text');
/* 'currentLevelIndex + 1' replaces event 'currentElement' */
levelText.textContent = document.querySelector(`#star${currentLevelIndex + 1}`).alt;
}
/* Captcha level number - default is 1 */
const getNumber = str => Number(str.match(/\d+/)[0]) || 1;
/* Star index is always one number lower than level number (indexing rules) */
const getStarIndex = event => getNumber(event.target.id) - 1;
let stars = document.querySelectorAll('.star');
const handleStarClick = event => {
/* FIRST - blocking possibility to change star behaviour by mouse events */
gameLevel.removeEventListener('mouseover', highlightStars);
gameLevel.removeEventListener('mouseout', highlightStars);
/* SECOND - making all needed star with yellow color */
const stars = document.querySelectorAll('.star');
for (let i = 0; i <= getStarIndex(event); i++) {
stars[i].classList.add('yellow');
}
};
const highlightStars = event => {
const starIndex = getStarIndex(event);
updateAltText(starIndex);
for (let i = 1; i <= starIndex; i++) {
const star = document.querySelector(`#star${i + 1}`);
star.classList.toggle('yellow');
}
};
// resetBtn.addEventListener('click', highlightStars(`#star1`), true);
resetBtn.addEventListener('click', updateAltText(0), true);
const gameLevel = document.querySelector('.game-level');
gameLevel.addEventListener("mouseover", highlightStars);
gameLevel.addEventListener("mouseout", highlightStars);
gameLevel.addEventListener('click', handleStarClick, {once: true});
}
.stars {
display: flex;
margin: 10px auto;
width: 500px;
}
input[type='image'] {
width: 60px;
height: 60px;
border: thin solid black;
}
.yellow {
background-color: yellow;
}
.game-level {
display: flex;
width: 300px;
height: 100%;
}
.level-block {
display: flex;
width: 200px;
margin-left: 10px;
justify-content: center;
align-items: center;
border: 1px solid hsl(217, 86%, 50%);
border-radius: 25px;
background-color: hsl(212, 29%, 80%);
}
.level-block > span {
font-size: 18px;
}
.reset {
width: 80px;
height: 80px;
}
<div class="stars">
<div class="game-level">
<input type="image" class="star yellow" id="star1" src="" width="60" alt="easy level">
<input type="image" class="star" id="star2" src="" width="60" alt="normal level">
<input type="image" class="star" id="star3" src="" width="60" alt="hard level">
<input type="image" class="star" id="star4" src="" width="60" alt="very hard level">
<input type="image" class="star" id="star5" src="" width="60" alt="impossible level">
</div>
<div class="level-block">
<span id="level-text">Easy level</span>
</div>
</div>
<input type="button" class="reset" id="update" value="RESET">
The following demo uses JavaScript for click events only, all mouse events (ie hover) are pure CSS. The reset behavior simply removes .active class on all buttons then adds .active class to the first button. Instead of the first button title being displayed after a reset -- the reset button title: "Game Reset" is displayed, it might be a little confusing for users if there's no confirmation of a reset. Other behavior is included in demo that is logical and consistent such as toggling, hovering to a temporary state and clicking for a persistent state etc. Details are commented in demo.
// Reference the form
const stars = document.forms.stars;
/*
Register the form to the click event -- when a click occurs anywhere on or within the form, callback function twinkle() is
called
*/
stars.onclick = twinkle;
/**
//A -- twinkle passes a reference to the Event Object... (e)
//B1 - Two Event Object properties are used to reference:
The tag the was clicked by user: event.target
The tag registered to the event: event.currentTarget
//B2 - The HTMLFormElement property: .elements collects all form
controls into a Live HTML Collection (aka NodeList)
//C -- ui.star is a Collection of form controls with [name=star]
The brackets [] and spread operator ... converts the
NodeList into an Array
//D -- Reference the message tag. If the clicked tag was the reset
button -- for...of loop iterates through each [name=star]
and removes the class .active from all [name=star]
//E1 - Next add .active class to the default button
//E2 - Set the legend.message text to the value of clicked button
[title] attribute...
~~~~~~~
//F -- ...But if a button.star was clicked, a check to verify if
clicked tag has the .active class -- then a for...of
loop identical to the one described in line D is used to
remove any .active class.
//G -- After there are no .active, the Boolean declared in line F
determines whether the clicked tag gets the .active class
and its [title] attribute displayed or not
*/
function twinkle(e) {
const active = e.target;
const ui = e.currentTarget.elements;
const starZ = [...ui.star];
const msg = document.querySelector(".message");
if (active.matches("#clear")) {
for (let star of starZ) {
star.classList.remove("active");
}
ui.star1.classList.add('active');
msg.textContent = active.title;
} else if (active.matches(".star")) {
let status = active.classList.contains("active");
for (let star of starZ) {
star.classList.remove("active");
}
if (!status) {
active.classList.add("active");
msg.textContent = active.title;
} else {
active.classList.remove("active");
msg.textContent = "";
}
}
return false;
}
:root {
font: 400 small-caps 2.5vw/1 Arial
}
.levels {
display: table;
width: 96%;
height: auto;
border: 1px solid hsl(217, 86%, 50%);
border-radius:4px;
}
.message {
display: table-caption;
width: 40vw;
height: 6vh;
margin: 0 auto 2vh;
padding: 0.5vh 0;
border: 1px solid hsl(217, 86%, 50%);
border-radius: 1.5rem;
background-color: hsla(212, 29%, 80%, 25%);
text-align: center;
font-size: 1.5rem;
color: #0078D7;
}
#clear {
float: right;
transform: rotate(45deg);
padding: 0;
border: none;
background: none;
font-size: 3.5rem;
cursor: pointer;
}
#clear:focus {
outline: 0;
}
/*
Flex is applied to the button.star'S parent tag so the order
property can be utilized.
*/
.flex {
display: flex;
justify-content: space-evenly;
align-items: center;
width: 70vw;
}
.star {
display: table-cell;
position: relative;
width: 16vw;
height: 24vh;
border: thin solid black;
background: #DDD;
font-size: 3.75rem;
text-align: center;
vertical-align: middle;
cursor: pointer;
}
/*
GSC (General Sibling Combinator: ~ ) provides highlighting across
multiple buttons.
Exp. 5 buttons: [-] [-] [X] ~ [>] ~ [>]
*/
.star.active,
.star:hover,
.star.active ~ .star,
.star:hover ~ .star {
background: gold;
}
/*
HTML layout has button.star in reverse order. Applying order to
each button rectifies the order by APPEARING in order while the
HTML structure remains reversed.
*/
#star1 {
order: 1;
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
}
#star2 {
order: 2;
}
#star3 {
order: 3;
}
#star4 {
order: 4;
}
#star5 {
order: 5;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
#star1:hover,
#star1.active {
color: #5BC0DE;
}
#star2:hover,
#star2.active {
color: #FF1C8D;
}
#star3:hover,
#star3.active {
color: #00D800;
}
#star4:hover,
#star4.active {
color: #0000D5;
}
#star5:hover,
#star5.active {
color: #D50000;
}
<form id="stars" action="">
<fieldset name="levels" class="levels">
<legend class="message">Novice</legend>
<button id="clear" type="reset" title="Game Reset">π</button>
<section class="flex">
<button id="star5" name='star' class="star" title="Master">π</button>
<button id="star4" name='star' class="star" title="Expert">π</button>
<button id="star3" name='star' class="star" title="Advanced">π</button>
<button id="star2" name='star' class="star" title="Intermediate">π</button>
<button id="star1" name='star' class="star active" title="Novice">π</button>
</section>
</fieldset>
</form>
Here is the method Iβm trying to make. Basically what itβs supposed to do is, when an <input> with the type of button is clicked, it makes the next <div> (in this case hard-coded) go from display: none to display: block. However itβs not working.
matchDivs() {
let showInputs = document.querySelectorAll('input')
const inputs = Array.from(showInputs)
inputs.map(input => {
if (input.parentNode.getAttribute('class') === 'first-employee') {
document.querySelector('.second-employee').setAttribute('style', 'display: block')
}
return input
})
}
When you use if (node.getAttribute('class') === 'first-employee') this is return:
class='first-employee' // true
class='employee first-employee' // false
You must use:
if (node.classList.contains("first-employee")):
class='first-employee' // true
class='employee first-employee' // true
If I have understand your question properly that on button click you want to show/hide DIV tag next to it, which is inside common parent div for both, button and 'second-employee'.
I think below will be helpful.
// For single Div show/hide on button click
let btnMatchDiv = document.querySelector('#btnMatchDivs');
btnMatchDiv.addEventListener('click', matchDivs, true);
function matchDivs() {
let showInputs = document.querySelectorAll('input')
const inputs = Array.from(showInputs)
inputs.map(input => {
// Method 1
// ===========================
/*if (input.parentNode.getAttribute('class') === 'first-employee') {
document.querySelector('.second-employee').setAttribute('style', 'display: block')
}*/
// Method 2 : you can use new classList method
// ===========================
if (input.parentNode.classList.contains('first-employee')) {
input.nextElementSibling.classList.toggle('hidden');
}
//return input
})
}
body {
font-family: Arial;
}
.first-employee {
display: block;
padding: 1em;
border: 1px solid green;
}
.second-employee {
padding: 1em;
border: 1px solid red;
}
#btnMatchDivs {
padding: 1em 0.5em;
border: 1px solid #777;
}
.hidden {
display: none
}
<div class="first-employee">
<h2>
First Employee
</h2>
<input type="button" id="btnMatchDivs" value="Toggle Second Employee" />
<div class="second-employee hidden">
Second Employee
</div>
</div>
Please let me know if you need further help on this.
Thanks,
Jignesh Raval
Also if you want to toggle multiple items then you can try below code.
// For multiple Div show/hide on button click
// ===================================
let showInputs = document.querySelectorAll('input')
const btnInputs = Array.from(showInputs)
// Bind click event to each button input
btnInputs.map(input => {
input.addEventListener('click', matchDivs, false);
})
function matchDivs(event) {
let buttonEle = event.currentTarget;
if (buttonEle.parentNode.classList.contains('first-employee')) {
buttonEle.nextElementSibling.classList.toggle('hidden');
}
}
body {
font-family: Arial;
}
.first-employee {
display: block;
padding: 1em;
border: 1px solid green;
margin-bottom: 1em;
}
.second-employee {
margin: 1em 0;
padding: 1em;
border: 1px solid red;
}
#btnMatchDivs {
padding: 1em 0.5em;
border: 1px solid #777;
}
.hidden {
display: none
}
<div class="first-employee">
<h2>
First Employee 1
</h2>
<input type="button" id="btnMatchDivs" value="Match Divs" />
<div class="second-employee hidden">
Second Employee 1
</div>
</div>
<div class="first-employee">
<h2>
First Employee 2
</h2>
<input type="button" id="btnMatchDivs" value="Match Divs" />
<div class="second-employee hidden">
Second Employee 2
</div>
</div>
<div class="first-employee">
<h2>
First Employee 3
</h2>
<input type="button" id="btnMatchDivs" value="Match Divs" />
<div class="second-employee hidden">
Second Employee 3
</div>
</div>
I hope this will be helpful.
Thanks,
Jignesh Raval
I apply the animate in one product which it show in the code i want to make two or more product to do the same animate when user click more info
$(document).ready(function() {
var fishPath = "http://upload.wikimedia.org/wikipedia/commons/7/77/Puffer_Fish_DSC01257.JPG";
var $con = $('#con');
var $con2 = $('#con2');
var $conImage = $('#image');
var $conLink = $('#link');
var $conLink2 = $('#link2');
$conImage.attr('src', fishPath);
$conLink2.hide(); //hide second link immediately
$conLink.click(function() {
$con2.addClass('clicked');
$con.animate({
height: "+=290",
width: "+=460"
}, 800);
$conImage.animate({
width: 220
}, 800);
$(this).fadeOut(100, function() {
$conLink2.fadeIn(800);
});
});
//hide button
$conLink2.click(function() {
$con.removeClass('conClicked');
$con.animate({
height: "-=290",
width: "-=460"
}, 800);
$conImage.animate({
width: 150
}, 800);
$(this).fadeOut(800, function() {
$conLink.fadeIn(800);
});
});
});
body {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#instructions {
font-size: smaller;
font-style: italic;
}
#image {
width: 150px;
}
#con2 {
display: inline-block;
border: 1px solid red;
padding: 10px;
}
.conClicked {
margin: 10px 5px 5px 18px;
width: 250px;
height: 260px;
}
#con {
border: 1px solid blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="instructions">
I have some data in my database 'phpmyadmin' i selected all data but i want to make some animation when user click more info it increase width and height but when i do so it animate the first only not all i make it Id cuz i need one product which user
select but class make it all animated in the same time, here the code
</p>
<div id="con2">
<div id="con">
<img id="image" src="fish/'.$r['image_p'].'">
<h3>'.$r['name_p'].'</h3>
<h4>Num Of Product :'.$r['id'].'</h4>
<h4>Details: '.$r['details'].'</h4>
<h4>Amount: 1GK</h4>
<h4>Price: '.$r['price'].' P</h4>
<button id="link">MORE INFO</button>
<button id="link2">LESS INFO</button>
</div>
</div>
From this code I want to do more than one product which user click 'more info' from any product it animate.
instead of using id of the button for doing the animation, try using the class. you just need one button per product to do this. try this jsfiddle http://jsfiddle.net/98rudLkc/