HTML/jQuery Limit no. of divs in container - javascript

(firstly I recommend seeing the related image)
I have 3 container having same class .container. Also, user can add child divs dynamically into the containers. The user will start adding divs (that is .parent) by clicking '.add' div inside each '.parent'.
The containers can have at max 3 div. If the user has added 3 divs already then the next div should go in the second container and so on. Once the last container(the third one) is full, an alert should pop up saying "You cannot add anymore divs."
I have two questions:
Using jquery how can I limit the number of '.parent' divs per container to 3. If the user tries to add another it is added to container 2 (unless container 2 has 3 child divs, then it would go to container 3)?
Once the container of page 3 is full (3 divs) an alert should pop up saying "You cannot add anymore divs".
The only snippet of code that I have is not working. Please help me with the code. I am novice in all this stuff.
Thanks in advance.
Related image: [1]: http://i.stack.imgur.com/zi78d.png
Sample code:
<html>// the containers
<div class="container"></div>
<div class="container"></div>
<div class="container"></div>//divs that are supposed to be appended
<div class="parent">
<div class="add"></div>
</div>
<div class="parent">
<div class="add"></div>
</div>
<div class="parent">
<div class="add"></div>
</div>
<div class="parent">
<div class="add"></div>
</div>. . .
</html>
.
<script>
var $pages = $('.container');
var child = '$('.add ').parent()';
$(".add").on('click', function () {
var childAdded = false;
$pages.each(function () {
var $container = $(this);
if ($container.children().length < 3) {
$container.append.('child');
childAdded = true;
return false;
}
});
if (!childAdded) {
alert("You can not add any more divs");
}
});
</script>

Several problems in your code
You want the instance of the parent class when you click on the button, not all .parent
You have syntax errors using quotes around jQuery objects that shouldn't be there.
Here's a simple approach using filter() method.
$(".add").on('click', function () {
/* filter $pages down to first available one with space */
var $page=$pages.filter(function(){
return $(this).children().length < 3;
}).first();
if( !$page.length ){ /* if no elements returned from filter, they are all full */
alert("You can not add any more divs");
}else{
/* get instance of parent based on button that was clicked which is "this" */
var $parent=$(this).closest('.parent');
$page.append( $parent );
}
});
DEMO
filter() API docs

You have to track the amount of divs you have added yourself. Then, use this information to determine which .container you should put it in. Something like this:
var added = 0;
...
$(".add").on('click', function () {
var target;
if(added<3) {
target = $pages[0];
} else if (added<6) {
target = $pages[1];
} else if (added<9) {
target = $pages[2];
} else {
alert("You can not add any more divs");
return
}
$(target).append($(this).parent());
added += 1;

Related

Click outside an element doesn't work

I have this code:
function showAll(el){
var id = el.parentNode.id;
var all= document.getElementById(id).getElementsByClassName('items')[0];
if(all.style.display === 'block'){
all.style.display = 'none';
} else{
all.style.display = 'block';
window.addEventListener('mouseup', function(e){
document.getElementById('test').innerHTML = e.target.className;
if(e.target != all){
all.style.display = 'none';
}
});
}
}
<div id="parent">
<div class="selected" onClick="showAll(this);">
</div>
<div class="items" style="display: none">
</div>
</div>
Basically what i want to achieve is: click on selected to display items which is now hidden after that if i click again on selected or if i click outside of items(a random spot on that page or even on selected) i want to be able to hide items.
The problem is that without the EventListener when i click on selected it works to display items and then if i click again on selected it works to hide items but if i click on a random spot it doesn't work to close items.
But when i add EventListener and i click on selected it works to click a random spot to close items but it doesn't work to click selected again to close items.
Can anybody help me with a full JavaScript explanation, please?
You're going to want to use highly reusable code. I use change() and id_() on my web platform all of the time and it's very direct and simple. In the below example the second parameter will make the class empty (you can also use id_('items').removeAttribute('class') for a cleaner DOM (Document Object Model)).
HTML
<input onclick="change(id_('items','');" type="button" value="Display Items" />
<div clas="hidden" id="items"><p>Items here.</p></div>
CSS
.hidden {display: none;}
JavaScript
function change(id,c)
{
if (id_(id)) {id_(id).className = c; if (id_(id).className=='') {id_(id).removeAttribute('class');}}
else if (id) {id.className = c; if (id.className=='') {id.removeAttribute('class');}}
else {alert('Error: the class id \''+id+'\' was not found or has not yet been imported to the DOM.\n\nNew class intended: '+c);}
}
function id_(id)
{
if (id == '' && window['console']) {console.log('Developer: empty id called from: '+id_.caller.toString().split('function ')[1].split('(')[0]);}
return (document.getElementById(id)) ? document.getElementById(id) : false;
}
This code exists from years of refining the same platform instead of industry standard drama of pointlessly changing things. You are two clicks from finding more highly reusable functions on my platform's JavaScript documentation from the link in my profile.

Select a div inside a parent two levels up?

I have some hidden sections which expand when an arrow is clicked. The arrow used to be a part of the main "questionBlock" div, so we used $content = $arrow.siblings(.questionBlockExpand) to select the section to expand. But now with some required layout changes, the arrow is now apart of the title which is within another div called "questionBlockTitle".
I am having trouble selecting the "questionBlockExpand" which is now within the parent div 2 levels up.
The JQuery code below is one of my attempts to select div via the 2nd parent level.
What am I doing wrong?
Thanks.
HTML:
<div class='questionBlock testing123 '>
<div class='questionBlockTitle'><span class='sectionQuestionBlockNumber'>1</span><span class='sectionQuestionBlockTitle'>title text</span>
<div class='expandArrow downArrow'>Expand Arrow Image</div>
</div>
<div class='questionBlockExpand'>Section to expand</div>
</div>
JQuery:
$(".expandArrow").click(function () {
$arrow = $(this);
$content = $arrow.parent().parent(".questionBlockExpand");
//open up the content needed - toggle the slide- if visible, slide up, if not slidedown.
$content.slideToggle(500, function () {
//execute this after slideToggle is done
//change class of header based on visibility of content div
if ($content.is(":visible")) {
$arrow.addClass("upArrowBlock");
$arrow.removeClass("downArrowBlock");
} else {
$arrow.addClass("downArrowBlock");
$arrow.removeClass("upArrowBlock");
}
});
});
You can use closest() to traverse up to container questionBlock div the use find()
$content = $arrow.closest('.questionBlock ').find(".questionBlockExpand");
OR,
$content = $arrow.closest('.questionBlockTitle').next(".questionBlockExpand");
//$content = $arrow.parent().next(".questionBlockExpand");
OR
$content = $arrow.closest('.questionBlockTitle').siblings(".questionBlockExpand");
//$content = $arrow.parent().siblings(".questionBlockExpand");

How do I apply jQuery's slideToggle() to $(this) and do the opposite to all other elements?

What I'd like to do is have all elements of class collapsible_list not displayed by default (with one exception... see below*), and then toggle their display when their parent <div class="tab_box"> is clicked. During the same click, I'd also like for every other element of class collapsible_list to be hidden so that only one of them is expanded at any given time.
*Furthermore, when the page initially loads I'd also like to check to see if an element of collapsible_list has a child a element whose class is activelink, and if there is one then I'd like that link's parent collapsible_list element to be the one that's expanded by default.
Here's some sample html code:
<style>
.collapsible_list {
display: none;
}
.collapsible_list.active {
display: block;
}
</style>
<div id="sidebar">
<div class="tab_box">
<div class="collapsible_tab">2014</div>
<div class="collapsible_list panel-2014">
1
2
3
</div>
</div>
<div class="tab_box">
<div class="collapsible_tab">2013</div>
<div class="collapsible_list panel-2013">
<a class="activelink" href="/2013/1">1</a>
2
3
</div>
</div>
</div>
And here's where I'm currently at with the javascript (although I've tried a bunch of different ways and none have worked like I'd like them to):
$(document).ready(function() {
// This looks redundant to me but I'm not sure how else to go about it.
$(".collapsible_list").children("a.activelink").parent(".collapsible_list:not(.active)").addClass("active");
$(".tab_box").click(function() {
$(this).children(".collapsible_list").toggleClass("active").slideToggle("slow", function() {
$(".collapsible_list.active:not(this)").each(function() {
$(this).slideToggle("slow");
});
});
});
});
I hope that's not too confusing, but if it is then feel free to let me know. Any help is much appreciated.
Since you have a dom element reference that needs to be excluded use .not() instead of the :not() selector
jQuery(function ($) {
// This looks redundant to me but I'm not sure how else to go about it.
$(".collapsible_list").children("a.activelink").parent(".collapsible_list:not(.active)").addClass("active").show();
$(".tab_box").click(function () {
var $target = $(this).children(".collapsible_list").toggleClass("active").stop(true).slideToggle("slow");
//slidup others
$(".collapsible_list.active").not($target).stop(true).slideUp("slow").removeClass('active');
});
});
Also, instead of using the slide callback do it directly in the callback so that both the animations can run simultaniously
Also remove the css rule .collapsible_list.active as the display is controlled by animations(slide)
Try This.
$('.collapsible_tab a').on('click', function(e){
e.preventDefault();
$('.collapsible_list').removeClass('active')
$(this).parent().next('.collapsible_list').toggleClass('active');
});
Fiddle Demo
I think your code would be less complicated if you simply remembered the previously opened list:
jQuery(function($) {
// remember current list and make it visible
var $current = $('.collapsible_list:has(.activelink)').show();
$(".tab_box").on('click', function() {
var $previous = $current;
// open new list
$current = $('.collapsible_list', this)
.slideToggle("slow", function() {
// and slide out the previous
$previous.slideToggle('slow');
});
});
});
Demo

Simple Way to Switch a CSS Class in Javascript

I appreciate all the suggestions I've gotten so far-thank you!
I'll try to describe a bit better what I'm trying to do:
I want to switch a CSS class on the active (clicked on) tab item on a item (to make a highlight effect while its related content is showing).
The JS Fiddle http://jsfiddle.net/4YX5R/9/ from Vlad Nicula comes close to what I'm trying to achieve, however I can't get it to work in my code.
The tabs are linked to content which is shown on the page when the tab is clicked. This part is working fine. I just want to change the CSS style on the ContentLink items when its content is being shown.
I'd also like to keep the content for ContentLink1 visible when the page loads, as it is now in the code, and for ContentLink1 to have the CSS .infoTabActive class when the page loads. When the ContentLink tab is not clicked, it should have the .infoTab class.
This is what I have so far:
HTML:
<article class="grid-70 infoContainer">
<a class="infoTab" id="aTab" href="javascript:show('a')">ContentLink1</a>
<a class="infoTab" id="bTab" href="javascript:show('b')">ContentLink2</a>
<a class="infoTab" id="cTab" href="javascript:show('c')">ContentLink3</a>
<div id="a">
<p> Inhalt 1111111.</p></div>
<div id="b">
<p>Inhalt 222222222
</p></div>
<div id="c">
<p>Inhalt 33333333
<7p></div>
</article>
Javascript:
window.onload = function () {
document.getElementById("a").style.display = "block";
}
function show(i) {
document.getElementById('a').style.display ="none";
document.getElementById('b').style.display ="none";
document.getElementById('c').style.display ="none";
document.getElementById(i).style.display ="block";
}
basic CSS for tab styles I want to apply:
.infoTab {
text-decoration:none;
color:red;
}
.infoTabActive {
text-decoration:none;
color:yellow;
}
Any pointers would be appreciated!
You can switch the classes simply bu using class property on DOM element.
To replace the existing class use
document.getElementById("Element").className = "ClassName";
Similarly to add a new class to exisiting classes use
document.getElementById("Element").className += "ClassName";
Change show function to be like this:
function show(i) {
document.getElementById('a').style.display ="none";
document.getElementById('a').className ="";
document.getElementById('b').style.display ="none";
document.getElementById('b').className ="";
document.getElementById('c').style.display ="none";
document.getElementById('c').className ="";
document.getElementById(i).style.display ="block";
document.getElementById(i).className ="selected";
}
I changed a little bit your code to make it suits your needs.
First, change the onload part in the Fiddle, by no wrap.
Then, you need to hide each elements at start like this :
window.onload = function () {
var list = document.getElementsByClassName("hide");
for (var i = 0; i < list.length; i++) {
list[i].style.display = "none";
}
}
I added an hide class to achieve it. Your show function works well then.
I would do it like this:
add a class called .show which sets the element to display block.
then toggle the classname.
Here's a JSFiddle
And here's an example:
HTML
<article class="grid-70 infoContainer">
<a class="infoTab" id="aTab" href="javascript:show('a')">Werbetexte</a>
<a class="infoTab" id="bTab" href="javascript:show('b')">Lektorate</a>
<a class="infoTab" id="cTab" href="javascript:show('c')">Übersetzung</a>
<div class="box" id="a">
<div class="col1"> <p>Inhalt 1111111.</p></div>
</div>
<div class="box" id="b">
Inhalt 222222222
</div>
<div class="box" id="c">
Inhalt 33333333
</div>
</article>
JavaScript
window.onload = function () {
show('a');
}
function show(elm) {
// get a list of all the boxes with class name box
var shown = document.getElementsByClassName('box');
// loop through the boxes
for( var i=0; i<shown.length; i++ )
{
// set the classname to box (removing the 'show')
shown[i].className = 'box';
}
// change the classname to box show for the element that was clicked
document.getElementById( elm ).className = 'box show';
}
CSS
.box {
display:none;
}
.box.show {
display:block;
}
Simplest way I could think of is this : http://jsfiddle.net/4YX5R/9/
Basically you don't want to listen to each element. If you do that you will have issues with new tabs. If you listen to the parent element like in my example you can add new tabs without having to write any more javascript code.
<a class="infoTab" data-target='a' id="aTab">Werbetexte</a>
Each tab button has a data-target attribute that will describe the div to show as the tab content. Hiding and showing content will be done via css, not style - which is a recommended best practice -.
tabs.addEventListener("click", function ( ev ) {
var childTarget = ev.originalTarget || ev.toElement;
...
}
When a tab is clicked, we check to see which element was clicked from the event listener on the parent, and then get the data-target from it. We use this as a id selector to show the new tab. We also need a reference to the old tab that was active, so we can hide it.
The logic is not that complicated, and with this you can have any number of tabs. I would recommend jQuery for this, since the event delegation might not work in all browsers with the current code.
I hope this helps :)

How to detect mouseleave() on two elements at once?

Answer can be in vanilla js or jQuery. I want to hide a div with the id "myDiv" if the user is no longer hovering over a link with the id "myLink" or a span with the id "mySpan". If the user has his mouse over either element "myDiv" will still show, but the second the user is not hover over either of the two (doesn't matter which element the user's mouse leaves first) "myDiv" will disappear from the face of existence.
In other words this is how I detect mouse leave on one element:
$('#someElement').mouseleave(function() {
// do something
});
but how to say (in a way that will actually work):
$('#someElement').mouseleave() || $('#someOtherElement').mouseleave()) {
// do something
});
How to detect this?
Something like this should work:
var count = 0;
$('#myLink, #mySpan').mouseenter(function(){
count++;
$('#myDiv').show();
}).mouseleave(function(){
count--;
if (!count) {
$('#myDiv').hide();
}
});
jsfiddle
You could use a multiple selector:
$("#someElement, #someOtherElement").mouseleave(function() {
// Do something.
});
You can beautifully use setTimeout() to give the mouseleave() function some "tolerance", that means if you leave the divs but re-enter one of them within a given period of time, it does not trigger the hide() function.
Here is the code (added to lonesomeday's answer):
var count = 0;
var tolerance = 500;
$('#d1, #d2').mouseenter(function(){
count++;
$('#d3').show();
}).mouseleave(function(){
count--;
setTimeout(function () {
if (!count) {
$('#d3').hide();
}
}, tolerance);
});
http://jsfiddle.net/pFTfm/195/
I think, it's your solution!!!
$(document).ready(function() {
var someOtherElement = "";
$("#someElement").hover(function(){
var someOtherElement = $(this).attr("href");
$(someOtherElement).show();
});
$("#someElement").mouseleave(function(){
var someOtherElement= $(this).attr("href");
$(someOtherElement).mouseenter(function(){
$(someOtherElement).show();
});
$(someOtherElement).mouseleave(function(){
$(someOtherElement).hide();
});
});
});
----
html
----
<div id="someElement">
<ul>
<li>element1</li>
<li>element2</li>
</ul>
</div>
<div id="tab1" style="display: none"> TAB1 </div>
<div id="tab2" style="display: none"> TAB1 </div>
While the answer from lonesomeday is perfectly valid I changed my html to have both elements in one container. I originally wanted to avoid this hence I had to do more refactoring for my clients in other html templates, but I think it will pay out on the long term.
<div id="my-container">
<div class="elem1">Foo</div>
<div class="elem2">Bar</div>
</div>
$('#my-container').mouseleave(function() { console.log("left"); });

Categories

Resources