I'm trying to do a accordion when the click on "show" to show the full profile from a list of profiles and close it with another button.
So far what I've done
<div class="short-pro">
<button class="show">Full Profile</button>
</div>
<div class="full-pro">
<button>close</button>
</div>
<div class="short-pro">
<button class="show">Full Profile</button>
</div>
<div class="full-pro">
<button>close</button>
</div>
<div class="short-pro">
<button class="show">Full Profile</button>
</div>
<div class="full-pro">
<button>close</button>
</div>
Script:
var show = document.getElementsByClassName('show');
var full= document.getElementsByClassName('full-pro');
for (i = 0; i < accbtn.length; i++) {
show[i].addEventListener("click", activeBtn());
}
function activeBtn() {
for (i = 0; i < active.length; i++) {
full[i].classList.add("active");
}
}
Note: close button not tried yet.
I hope you get the idea of what I'm trying to achieve. Any help would be highly appreciated!
You have several problems:
You should mark variable i as let
You should pass i to activeBtn function
So, you code should be like this:
var show = document.getElementsByClassName('show');
var full = document.getElementsByClassName('full-pro');
var close = document.getElementsByClassName('close');
for (let i = 0; i < show.length; i++) {
show[i].addEventListener("click", function() {
activeBtn(i);
});
}
for (let i = 0; i < close.length; i++) {
close[i].addEventListener("click", function() {
deactiveBtn(i);
});
}
function activeBtn(i) {
full[i].classList.add("active");
}
function deactiveBtn(i) {
full[i].classList.remove("active");
}
You can see full example in sandbox: https://jsfiddle.net/73ktn6hc/3/#&togetherjs=t0jU1L4mI2
Related
Hi,
can anyone tell me why everytime i click in the yellow button console log shows me the value false?? (should be alternately false right false right etc after every click)
where's mistake?
var btn = document.querySelectorAll('.cdi-link');
var dropdown = document.getElementsByClassName('cdi-dropdown')
for (var i = 0; i < btn.length; i++) {
btn[i].addEventListener('click', function() {
var button = this;
var arrow = button.lastElementChild.lastElementChild;
var btnColor = button.lastElementChild;
var flag = true;
if (flag) {
flag = false;
console.log(flag);
} else {
flag = true;
console.log(flag);
}
});
}
<section class="cursos-de-ingles">
<article class="main-cdi">
<div class="cdi cdi-one">
<div class="cdi-header">
<img src="img/numero1.png" alt="">
</div>
<div class="cdi-par">
<button type="button" class="cdi-link">
<div>
<span>Ver detalles de cursos de Inglés General</span> <span class="arrow">▶</span>
</div>
</button>
</div>
<!--cdi-par-->
<img src="img/greybox.png" alt="">
</div>
<!--cdi-one-->
<div class="cdi cdi-two">
<button type="button" class="cdi-link">
<div>
<span>Ver detalles de los cursos de Inglés Académico</span> <span class="arrow">▶</span>
</div>
</button>
</div>
<!--cdi-par-->
<img src="img/greybox.png" alt="">
<!--cdi-two-->
<div class="cdi cdi-three">
<div class="cdi-header">
</div>
<div class="cdi-par">
<button type="button" class="cdi-link">
<div>
<span>Ver detalles de los cursos individuales</span> <span class="arrow">▶</span>
</div>
</button>
</div>
<!--cdi-par-->
<img src="img/greybox.png" alt="">
</div>
<!--cdi-three-->
Move your flag outside of eventListener and for loop, because on every click or loop you will set the flag to true.
var btn = document.querySelectorAll('.cdi-link');
var dropdown = document.getElementsByClassName('cdi-dropdown')
var flag = true;
for (var i = 0; i < btn.length; i++) {
btn[i].addEventListener('click', function() {
var button = this;
var arrow = button.lastElementChild.lastElementChild;
var btnColor = button.lastElementChild;
if (flag) {
flag = false;
console.log(flag);
} else {
flag = true;
console.log(flag);
}
});
}
but if you want a different flag for different clicks use a map instead:
var btn = document.querySelectorAll('.cdi-link');
var dropdown = document.getElementsByClassName('cdi-dropdown')
var flags = {};
for (var i = 0; i < btn.length; i++) {
btn[i].addEventListener('click', function() {
var button = this;
var arrow = button.lastElementChild.lastElementChild;
var btnColor = button.lastElementChild;
if (flag[i]) {
flag[i] = false;
console.log(flag[i]);
} else {
flag[i] = true;
console.log(flag[i]);
}
});
}
This is because the flag is always set as true when the click happens, therefore it keeps setting itself back to false. The declaration of the flag should be moved outside of the click handler.
I have the following that is supposed to hide/show the array values with a delay of 3seconds:
function loop(s, val) {
s.style.display = val;
if (++i < s.length) {
setTimeout(loop, 3000); // call myself in 3 seconds time if required
}
};
Autocomplete.prototype.filterCategory = function(e) {
this.input.addEventListener("keyup", (e) => {
let inputVal = this.input.value,
patt = new RegExp(inputVal);
for (var i = 0; i < this.categories.length; i++) {
if(!patt.test(this.categories[i].dataset.cat)){
//this.categories[i].style.display = "none";
console.log('none');
loop(this.categories[i], 'none');
}
else {console.log('block');
loop(this.categories[i], 'block');
}
}
});
}
Short HTML:
<div class="category" data-cat="programmer, sales, manager">
<p class="h-text-cap">sales</p>
<a class="btn btn__open btn__open--blue" href="">more</a>
</div>
<div class="category" data-cat="programmer, sales, manager, vendor">
<p class="h-text-cap">sales</p>
<a class="btn btn__open btn__open--blue" href="">more</a>
</div>
<div class="category" data-cat="programmer, sales, manager, carpenter">
<p class="h-text-cap">capenter</p>
<a class="btn btn__open btn__open--blue" href="">more</a>
</div>
<div class="category" data-cat="programmer, sales">
<p class="h-text-cap">sales</p>
<a class="btn btn__open btn__open--blue" href="">more</a>
</div>
UPDATED CODE:
function loop(s, val) {
s[i].style.display = val;
if (++y < s.length) {
setTimeout(loop(s, val), 3000); // call myself in 3 seconds time if required
}
};
Autocomplete.prototype.filterCategory = function(e) {
this.input.addEventListener("keyup", (e) => {
let inputVal = this.input.value,
patt = new RegExp(inputVal),
newA = [];
for (var i = 0; i < this.categories.length; i++) {
if(!patt.test(this.categories[i].dataset.cat)){
newA.push(this.categories[i]);
loop(newA, 'none');
}
else {
loop(this.categories[i], 'block');
}
}
});
}
i is not defined
by typing a job-title "programmer", I want o hide all the data-cat that don't hold that value. At the moment it works fine in terms of logic, however they all get hidden or shown in one block as opposed to individually with a 3s delay.
<div id="wrapper">
<div class="quotes">
<p id="par"></p>
</div>
<button class="btn" onClick="randomQuote()">button</button>
</div>
function randomQuote () {
var array = [1,20,50,100];
}
document.getElementById("btn").onclick = randomQuote;
document.getElementById("par").innerHTML = array[0];//then on another btn click array[1]...
for(var i=0; i<array.length;i++){
quote[i];
}
On "btn" click number 1 from array is shown in "par" paragraph
on another btn click number 2 shows up and 1 dissapear, and so on...
Use counter cpt as index to loop through the array and show the values :
var array = [1,20,50,100];
var cpt = 0;
//Init the 'par' div before click
document.querySelector("#par").innerHTML = array[cpt];
function randomQuote ()
{
if(cpt<array.length-1)
cpt++;
else
cpt=0;
document.querySelector("#par").innerHTML = array[cpt];
}
<div id="wrapper">
<div class="quotes">
<p id="par"></p>
</div>
<button class="btn" onClick="randomQuote()">button</button>
</div>
Minified version could be :
function randomQuote ()
{
document.querySelector("#par").innerHTML = array[cpt<array.length-1?++cpt:cpt=0];
}
Snippet using Random color as you comment say :
var array = ["Quotes 1","Quotes 2","Quotes 3","Quotes 4"];
var cpt = 0;
//Init the 'par' div before click
document.querySelector("#par").innerHTML = array[cpt];
//Init Random Color before click
getRandomColor();
function randomQuote()
{
if(cpt<array.length-1)
cpt++;
else
cpt=0;
document.querySelector("#par").innerHTML = array[cpt];
}
function getRandomColor()
{
document.querySelector("#par").style.backgroundColor = '#'+Math.floor(Math.random()*16777215).toString(16);
}
<div id="wrapper">
<p id="par"></p>
<button id="btn" onClick="randomQuote();getRandomColor()">Next quote</button>
</div>
Is this what you want?
var counter = 0;
function randomQuote () {
var array = [1,20,50,100];
document.getElementById("par").innerHTML(array[counter++]);
}
save the index, increment it on each click and then reset it when its undefined.
var index = -1;
function randomQuote() {
var array = [1, 20, 50, 100];
document.getElementById('par').innerText = (array[++index] || array[index=0]);
}
<div id="wrapper">
<div class="quotes">
<p id="par"></p>
</div>
<button class="btn" onClick="randomQuote()">button</button>
</div>
I want to show/hide div with children on button click, but not other same divs in different parent blocks.
Sorry for my bad explanation and js knowledge, maybe the code can speak better:
for (a = 0; a < document.querySelectorAll("#hidereplies").length; a++) {
var btn = document.querySelectorAll("#hidereplies")[a];
btn.onclick = function () {
for (var y = 0; y < document.querySelectorAll(".reply_comments").length; y++) {
var reply = document.querySelectorAll(".reply_comments")[y];
reply.style.display = (reply.style.display != 'none' ? 'none' : 'block');
}
};
}
Demo on jsfiddle.
There's a few things you're doing wrong.
First, in your HTML, don't use an ID more than once. You've given your buttons the same ID.
Next, assign your querySelector results to an array and iterate the array.
Third, you need to scope your query. You're checking for elements on the document so everything gets pulled in rather than being scoped to the current div.
//note that I've changed from an ID to a class for your buttons
var buttons = document.querySelectorAll(".hidereplies");
for (a = 0; a < buttons.length; a++) {
var btn = buttons[a];
btn.onclick = function (event) {
var btn = event.currentTarget; //get the current button clicked
var comments = btn.parentNode.querySelectorAll(".reply_comments"); //scope the search
for (var y = 0; y < comments.length; y++) {
var reply = comments[y];
reply.style.display = (reply.style.display != 'none' ? 'none' : 'block');
}
};
}
HTML
<div class="comments_list">
<div class="comment_item">
<div class="comment_body">test1 - comments</div>
<input type="button" class="hidereplies" value="show replies" />
<div class="reply_comments">
<div class="comment_body">want to hide only current ".reply_comments"</div>
</div>
</div>
<div class="comment_item">
<div class="comment_body">test2 - comments</div>
<input type="button" class="hidereplies" value="show replies" />
<div class="reply_comments">
<div class="comment_body">but not all</div>
</div>
</div>
<div class="comment_item">
<div class="comment_body">test3 - no comments</div>
<div class="reply_comments"></div>
</div>
</div>
Your updated fiddle - http://jsfiddle.net/yhtKa/4/
I have 2 sets of elements:
<div class='container container1'>
<div class='colors'>
<div class='blue'></div>
<div class='red'></div>
</div>
<div class='drinks'>
<div class='soda'>coke</div>
<div class='juice'></div>
</div>
</div>
<div class='container container2'>
<div class='cars'>
<div class='sedans'></div>
<div class='vans'></div>
</div>
<div class='drinks'>
<div class='soda'>mountain dew</div>
<div class='coffee'></div>
</div>
</div>
I want to paste container1 over container2 such that any replacements are over written and any uniques to each container are put left alone and put together.
The result should be:
<div class='container container-result'>
<div class='colors'>
<div class='blue'></div>
<div class='red'></div>
</div>
<div class='cars'>
<div class='sedans'></div>
<div class='vans'></div>
</div>
<div class='drinks'>
<div class='soda'>coke</div>
<div class='juice'></div>
<div class='coffee'></div>
</div>
</div>
The elements can have any arbitrary hierarchy / depth. What's the best way to do this?
Thanks in advance.
Since your question is tagged jQuery here's a slightly shorter answer using that library:
function copy(from, to) {
from.children().each(function() {
var match = to.children("." + this.className.split(' ').join('.'));
if(match.length) {
if(match.children().length == 0) {
match.replaceWith(this);
} else {
copy($(this), match);
}
} else {
to.append(this);
}
}).end().remove();
from.remove();
}
Then you'd just call it like this:
copy($(".container1"), $(".container2"));
You can give it a try here, the result is:
<div class="container container2">
<div class="cars">
<div class="sedans"></div>
<div class="vans"></div>
</div>
<div class="drinks">
<div class="soda">coke</div>
<div class="coffee"></div>
<div class="juice"></div></div>
<div class="colors">
<div class="blue"></div>
<div class="red"></div>
</div>
</div>
Note that the class name is still container2 if you want to replace that just add this to switch the class after the copy() call:
$(".container2").toggleClass("container2 container-result");
The match is based on all classes the element contains, so if an element has class="car blue" and there's a corresponding class="blue car" it'll choose that one to overwrite.
This isn't the most efficient route since you're firing up the selector engine on the children each iteration, but unless you're doing lots of elements, it should be pretty quick.
With regard to unique merging I can't help you there, but if your app by any chance happens to be in PHP then you can use php's array_merge function to merge them before outputting the HTML.
ReplaceWith is a nice jquery function to replace aka "paste" over, it may will help you with half of your solution.
This appears to do what you wanted:
<div class='container container1'>
<div class='colors'>
<div class='blue'></div>
<div class='red'></div>
</div>
<div class='drinks'>
<div class='soda'>coke</div>
<div class='juice'></div>
</div>
</div>
<div class='container container2'>
<div class='cars'>
<div class='sedans'></div>
<div class='vans'></div>
</div>
<div class='drinks'>
<div class='soda'>mountain dew</div>
<div class='coffee'></div>
</div>
</div>
<div class='container container-result'>
</div>
<script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
function getContainerArray(containers, level) {
level = level || 0;
var result = [];
for (var i=0; i<containers.length; ++i) {
var el = containers.eq(i);
var obj = { "class": el.attr("class") };
if (level == 0) {
obj.items = getContainerArray(el.children("div"), 1);
} else {
obj.text = el.text();
}
result.push(obj);
}
return result;
}
function mergeContainers(containerArray) {
var result = [];
function indexOfClass(name) {
for (var i = 0; i < result.length; ++i) {
if (result[i]["class"] == name) {
return i;
}
}
return -1;
}
for (var i = 0; i < containerArray.length; ++i) {
var obj = containerArray[i];
var name = obj["class"];
var index = indexOfClass(name);
if (index < 0) {
result.push(obj);
} else if (obj.items != null) {
result[index].items = mergeContainers(new Array().concat(result[index].items, obj.items));
}
}
return result;
}
function getHtml(objArray) {
var result = [];
for (var i = 0; i < objArray.length; ++i) {
var obj = objArray[i];
result.push("<div class=\"", obj["class"], "\">");
if (obj.text != null && obj.text != "") {
result.push(obj.text);
}
if (obj.items != null) {
result.push(getHtml(obj.items));
}
result.push("</div>");
}
return result.join("");
}
var html = getHtml(mergeContainers(getContainerArray($("div.container1>div,div.container2>div"))));
$("div.container-result").append(html);
</script>
This answer:
Does exactly what you asked for.
Handles repeated mergings, if div class container-result already exists.
Merges any number of container divs.
Uses jQuery and is more efficient than some other solutions.
See it in action at jsfiddle.net.
/*--- Get all "container" divs but exclude any "container-result" divs.
*/
var zContainers = $("div.container").not ("div.container-result");
if (zContainers && zContainers.length)
{
//--- Get or create the results div.
var zResultDiv = $("div.container-result");
if (!zResultDiv || !zResultDiv.length)
{
zResultDiv = zContainers.parent ().append ("<div class='container container-result'></div>");
zResultDiv = $("div.container-result");
}
//--- Move the container's contents to the master container, preserving order.
zContainers.each (function () {$(this).children ().appendTo (zResultDiv);} )
//--- Kill the old container(s).
zContainers.remove ();
RecursivelyMergeDivsByClass (zResultDiv);
}
function RecursivelyMergeDivsByClass (jNode)
{
/*--- Get a list of the direct-child div's class names.
Sort and winny out a list of duplicates.
*/
var zDirectChildDivs = jNode.find ("> div");
var aClassList = zDirectChildDivs.map (function () {return this.className;} ).get ();
aClassList.sort ().unshift (0);
for (var J = aClassList.length-1; J > 0; J--)
if (aClassList[J] != aClassList[J-1]) aClassList.splice (J, 1); // Delete items without duplicates.
aClassList.splice (0, 1);
/*--- For any duplicates, copy the contents into the first instance, preserving order.
For exact duplicate nodes, the first (oldest) version is kept and the remaining are discarded.
*/
for (var J = aClassList.length-1; J >= 0; J--)
{
var zDupClasses = zDirectChildDivs.filter ("." + aClassList[J]);
var zFirstDiv = zDupClasses.first ();
zDupClasses = zDupClasses.not (zFirstDiv);
zDupClasses.each (function () {$(this).children ().appendTo (zFirstDiv);} )
zDupClasses.remove ();
RecursivelyMergeDivsByClass (zFirstDiv)
}
}