I'm trying to update an array when clicking on the li tag. I've been testing, testing, and unable to come up with a solution.
I have two tabs: The "Create" tab opens a container that allows you to insert paragraphs, the "Home" tab opens another container containing a "Select paragraph" button.
Problem
It won't update the values of the array when I do it for the second time. I.e., if I switch between the tabs and go on select mode again to select/deselect, it will not update the new selection, instead I get the same selection from the first time.
I've created an example so you can look at it and if there is a better way to accomplish this (which I know there is) then be my guest. Link is below the next paragraph.
Instructions
In order to select a paragraph, first you need to add a paragraph which is created on the fly, then you switch to "Home" section, click on the "Select paragraph" button, this takes you to the "Create" section on select mode. To select/deselect, click on any paragraph. When you select a paragraph, it stores its position using jQuery - index() in the "storeClass" array. Once you're done selecting paragraphs then you exit the select mode by clicking "Ok" button and it switches to "Home" section, but let's say you want to create another paragraph then you click on "Create" tab, create the paragraph, switch to "Home" tab, go on select mode and select again and switch between tabs and you will see just the first selection you made on the first time.
Here is the same example: http://jsfiddle.net/7mbhnvas/8/
HTML
<ul class="tab">
<li><a class="paragraph-tab">Create</a></li>
<li><a class="select-tab">Home</a></li>
</ul>
<div class="wrap">
<div class="create-para-cont">
<h3>Create a paragraph</h3>
<ul class="para-results">
</ul>
<div class="para-tool">
<p><textarea class="textarea"></textarea></p>
<button type="button" class="create-para-button">Create paragraph</button>
<div>
<button type="button" class="select-ok-button">OK</button>
</div>
</div>
</div>
<div class="select-para-cont">
<h3>Select Mode</h3>
<p><button type="button" class="select-para-button">Select paragraph</button></p>
</div>
</div>
jQuery
$('ul.tab li a:first').addClass('tab-active');
$('.create-para-cont').addClass('cont-active');
$('.create-para-button').on('click', function(){
$('.create-para-cont').addClass('cont-active');
var parent = $('.para-results');
var child = $('<li></li>');
var p = $('<p></p>');
var textarea = $('.textarea').val();
if($('.create-para-cont').hasClass('cont-active')){
p.text(textarea);
child.append(p);
parent.append(child);
} else {
return false
}
});
var storeClass = [];
$('.select-para-button').on('click', function(){
$('.create-para-cont').addClass('cont-select');
if($('.para-results li').length >= 1){
$('.textarea, .create-para-button').hide();
$('.select-para-cont').hide();
$('.select-tab').removeClass('tab-active');
$('.create-para-cont').show();
$('.paragraph-tab').addClass('tab-active');
$('.select-ok-button').show();
for ( var i = 0; i < storeClass.length; i = i + 1 ) {
$('.para-results').each(function( index ) {
$( this ).find( "li:eq("+ storeClass[ i ] +")" ).addClass('p-selected');
});
}
}
});
$('ul.tab li').on('click','a', function(){
if($(this).hasClass('paragraph-tab')){
$('.para-results').children('li').removeClass('p-selected');
$('.select-para-cont').hide();
$('.select-tab').removeClass('tab-active');
$('.create-para-cont').show();
$('.paragraph-tab').addClass('tab-active');
} else {
$('.create-para-cont').removeClass('cont-active');
$('.create-para-cont').hide();
$('.paragraph-tab').removeClass('tab-active');
$('.select-para-cont').show();
$('.select-tab').addClass('tab-active');
}
});
$('ul.para-results').on('click','li', function(){
if($('.create-para-cont').hasClass('cont-select')){
$(this).toggleClass('p-selected');
var selected = $('.p-selected ');
var pSelected = selected.parent().children().index(this);
storeClass.push( pSelected );
} else {
return false;
}
});
$('.select-ok-button').on('click', function(){
if($('.create-para-cont').hasClass('cont-select')){
$('.create-para-cont').removeClass('cont-select');
$('.create-para-cont').removeClass('cont-active');
$('.create-para-cont').hide();
$('.paragraph-tab').removeClass('tab-active');
$('.select-para-cont').show();
$('.select-tab').addClass('tab-active');
}
});
I would reset storeClass to a blank array when the 'OK' button is clicked, and then re-push all the correct values into it in that same click handler:
$('.select-ok-button').on('click', function(){
if($('.create-para-cont').hasClass('cont-select')) {
storeClass = []; // make it blank
$('.p-selected').each(function() {
storeClass.push($(this).index()); // push each one into the array
});
....
}
});
Then your click handler of ul.para-results would look like this:
$('ul.para-results').on('click','li', function(){
if($('.create-para-cont').hasClass('cont-select')){
$(this).toggleClass('p-selected');
} else {
return false;
}
});
Here's an updated Fiddle
Related
I have this code,
HTML and php
<?php for($i = 1; $i <= 5; $i++) { ?>
<div class="file-add-row" style="display:none;">Some content</div>
<?php } ?>
<div id="add-file-plus">Add File</div>
and the JS is
$('#add-file-plus').live('click', function () {
if($('div.file-add-row:visible').length == 0) {
$('div.file-add-row:hidden:first').show();
} else {
$('.file-add-row:hidden:first').removeAttr("style").insertAfter($('.file-add-row:visible:last'));
}
});
Now, my problem is, when I click the add button for the first time, the first 'file-add-row' div is displayed. But when I click the add button the second time, nothing happens on the page. Instead, it just completely removes that div from the dom.
I am just a beginner in jquery, so there might be things I overlooked. Anyone got any idea about what's going on ?
When you do:
$('.file-add-row:visible:last')
Just after:
$('.file-add-row:hidden:first').removeAttr("style")
They both refer to the same object. And if you try to insert an object after itself, it will end up removing itself from the DOM.
Change the JS to:
$('#add-file-plus').live('click', function () {
if($('div.file-add-row:visible').length == 0) {
$('div.file-add-row:hidden:first').show();
} else {
var last_visible = $('.file-add-row:visible:last')
$('.file-add-row:hidden:first').removeAttr("style").insertAfter(last_visible);
}
});
Demo (Click on 'Add File'):
https://jsfiddle.net/woxd2jbf/1/
Here's a version without jQuery just plain JavaScript, it will work with divs just as it does with button, ul, and li. The details are commented within the source.
Key Methods
cloneNode()
appendChild()
PLUNKER
SNIPPET
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<!--This <li> is a template to clone-->
<li class="row" style='display:none'>Some content</li>
<!--This is the empty list to be populated with clones-->
<ul id='list'>
</ul>
<!--This button will have an eventListener
that will execute a function when it is clicked-->
<button id="add">Add File</button>
<script>
/* Reference each element involved in process */
// The button
var add = document.getElementById('add');
// The list
var list = document.getElementById('list');
// The first li
var row = document.querySelector('.row:first-of-type');
/*
1. Button will listen for a `click`
2. Create a clone of the first li
3. Add clone as the last child of list
4. Set clone's display property to block
so it's visible
*/
add.addEventListener('click', function(e) {
var clone = row.cloneNode(true);
list.appendChild(clone);
clone.style.display = 'block';
}, false);
</script>
</body>
</html>
$('.file-add-row:hidden:first').removeAttr("style").insertAfter($('.file-add- row:visible:last'));, due to this line the issue is happening. because it first removes the style attribute which makes it visible so $('.file-add- row:visible:last') is returning itself. refactor to store the visible el's reference like below:
$(function () {
$('#add-file-plus').live('click', function () {
if ($('div.file-add-row:visible').length == 0) {
$('div.file-add-row:hidden:first').show();
} else {
var $el = $('.file-add-row:visible:last');
$('.file-add-row:hidden:first').removeAttr("style").insertAfter($el);
}
});
})
I have a list <ul id="project_menu"> of items stacked vertically on the left, and every time an item is clicked from that list, a div (project-details) slowly fades in on the right side of the screen to display more info. Now, I only want one div to appear at a time, so if the user clicks item A, then item B, then item D, I want all other divs to disappear except the one last clicked(D). To that end, I wrote my own code in jQuery but it does not work.
Here are the relevant files:
<div class="project">
<ul id="project_menu" class="project_menu">
<li id="menu-php-mysql" data-projectID="php-project">PHP/MySQL</li>
<li id="menu-nodejs" data-projectID="node-project">NodeJS</li>
<!-- more code -->
</ul>
<div class="project-detail">
<div id="php-project">
<i class="ion-ios-close-empty close-icon js-close-icon"></i>
<div classs="project-text">
<!-- data about project -->
</div>
<div id="node-project">
<i class="ion-ios-close-empty close-icon js-close-icon"></i>
<div classs="project-text">
<!-- data about project -->
</div>
<!-- and so on.. -->
#php-project {
background-color: #9b59b6;
margin: 30px;
display: none;
}
$(document).ready(function() {
itemsToRender = [];
$('ul#project_menu li').click(function(e) {
menuItemId = (e.currentTarget.id);
itemsToRender.push(menuItemId);
var listSize = itemsToRender.length;
if (listSize > 1) {
for (var i = 0; i < listSize - 1; i++) {
currentItemId = itemsToRender[i];
$(getProjectId(currentItemId)).css('display', 'none');
}
menuItemId = itemsToRender.pop();
$(getProjectId(menuItemId)).fadeIn('slow');
} else {
$(getProjectId(menuItemId)).fadeIn('slow');
}
console.log(itemsToRender);
});
$('i.js-close-icon').click(function(e) {
projectId = (e.currentTarget.parentElement.id);
console.log(projectId);
$('#' + projectId).fadeOut('slow');
});
});
function getProjectId(menuItemId) {
if (menuItemId.indexOf('php') > 0) {
return '#php-project';
} else if (menuItemId.indexOf('node') > 0) {
return '#node-project';
} else if (menuItemId.indexOf('angular') > 0) {
return '#angular-project';
} else if (menuItemId.indexOf('mean') > 0) {
return '#mean-project';
}
}
What I tried to do was create an array which has all the item Ids that user clicked, and set all the corresponding divs's display to hidden except the last element of the array, which is the one displayed.
Now, if I load the page and click php, the corresponding div appears on the right.If I then click nodeJS, php div disappears and node div appears. If I click on the php div again, the php div appears, and it stacks on top of the node div (which doesn't disappear!). What am I doing wrong?
This is very easy, Just assign a class .common to all the items, then when you want to show one item, lets say #id3, you can do as follows.
// on event xyz....
jQuery(".common").hide();
jQuery("#id3").show();
Furthermore you can enhance by using the callback function as below.
// on event xyz....
jQuery(".common").hide(function(){
jQuery("#id3").show();
});
$('ul#project_menu li').click(function(e) {
$project_id = $(this).attr("data-projectID"); //Get the project id wish to display
$(".all_projects").hide(); //Hide all projects
$("#"+$project_id).show(); //Show lastly clicked project
}
give common class for all project divs. Here i given as all_projects
function tab_menu(){
if (buttonObject.value == value){
document.getElementById("div1").style.display = "block";
}
}
i was trying when click to buttons check Button Value and show a div based on Button's Value and hide others divs it should show just one div at same time. I wonder there is a javascript Master who can solve this problem.
SCRIPT:
function tabmenu(buttonObject){
var value = buttonObject.value
var target = document.getElementById(value);
if(target) {
var siblings = target.parentNode.getElementsByTagName("DIV");
for(i=0;i<siblings.length;i++){
siblings[i].style.display = "none";
}
target.style.display = "block";
}
}
HTML:
<div>
<div id="tab1">Tab1</div>
<div id="tab2">Tab2</div>
<div id="tab3">Tab3</div>
</div>
<button onclick="tabmenu(this);" value="tab1">Tab1</button>
<button onclick="tabmenu(this);" value="tab2">Tab2</button>
find the tab to activate (assuming value = tab.id)
find the parent and hence it's siblings (assuming they are DIVs)
hide the siblings
show the target
You can see it working here: http://jsfiddle.net/4rWdQ/
I know this has been asked a thousand times but I'm still having trouble figuring it out. I have a simple according following this tutorial and I'm trying to add an "expand/collapse all" link. I have found a way to expand all but can't figure out how to collapse them. The problem with a slideToggle() solution is if I open one then click the link it will close/open all of them but the one that was previoiusly active.
I set up a jsFiddle here.
Here is an overview of the code:
HTML:
<h2 class="acc_trigger">Div 1</h2>
<div class="acc_container">
<div class="block"> Yay content!</div>
</div>
<h2 class="acc_trigger">Div 2</h2>
<div class="acc_container">
<div class="block">More Content, Score!</div>
</div>
<h2 class="acc_trigger">Div 3</h2>
<div class="acc_container">
<div class="block">Even More Content</div>
</div>
Expand/Collapse All
JS:
jQuery(document).ready(function($) {
//Set default open/close settings
$('.acc_container').hide(); //Hide/close all containers
//On Click
$('.acc_trigger').click(function(){
$('.acc_trigger').removeClass('active').next().slideUp();
//Remove all "active" state and slide up the immediate next container
$(this).toggleClass('active').next().slideDown();
return false; //Prevent the browser jump to the link anchor
});
$('.acc_expand-all').click(function(){
//expand all on click
$('.acc_trigger').addClass('active').next().slideDown();
return false;
});
});
You have to check in your expand/collapse handler to see how many are open, etc., something like this (updated fiddle):
$('.acc_expand-all').click(function(){
var all = $('.acc_trigger'),
active = all.filter('.active');
if (all.length && all.length === active.length) {
// All open; close them
all.removeClass('active').next().slideUp();
}
else {
// At least some are closed, open all
all.not('.active').addClass('active').next().slideDown();
}
return false;
});
Regarding your question in the comments, you can check to see whether you should be in "exclusive" mode or not by checking how many are open and whether the clicked one is open, e.g. (updated fiddle):
$('.acc_trigger').click(function(){
var $this = $(this),
thisActive = $this.hasClass('active'),
active;
// If this one is active, we always just close it.
if (thisActive) {
$this.removeClass('active').next().slideUp();
}
else {
// Is there just one active?
active = $('.acc_trigger.active');
if (active.length === 1) {
// Yes, close it
active.removeClass('active').next().slideUp();
}
// Open this one
$this.addClass('active').next().slideDown();
}
});
I have a search box that as the user types letters into the search box, we will filter and display the results. However, as the user types each letter the search results are getting toggled between showing and hiding. I am very new to JS so I hope it could be an easy fix.
Here is my HTML:
See Below
Here is my toggle JS:
See Below
How can I tweak the JS to not toggle back and forth?
//Here are my edits to help answer the question. This is the JS and HTML I am using to display the results:
HTML:
<div class="friendssearch" onclick="toggle_visibility('friendslist');">
<div class="friendssearch">
<div id="friendssearchbox"></div>
</div>
<ul id="friendslist" style="display: none;">
<li>
<a href="#">
<div class="friendsflybox" title="Friends Name">
<p class="friendsflyboxname">Ryan Bennett</p>
</div>
</a>
</li>
</ul>
</div>
Javascript:
<script>
(function ($) {
// custom css expression for a case-insensitive contains()
jQuery.expr[':'].Contains = function(a,i,m){
return (a.textContent || a.innerText ||
"").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
function listFilter(header, list) { // header is any element, list is an unordered list
// create and add the filter form to the header
var form = $("<form>").attr({"class":"filterform","action":"#"}),
input = $("<input>").attr({"class":"filterinput clearFieldBlurred
ClearField","type":"text", "value":"Start typing a Name"});
$(form).append(input).appendTo(header);
$(document).ready(function() {
$('.clearField').clearField();
});
$(input)
.change( function () {
var filter = $(this).val();
if(filter) {
// this finds all links in a list that contain the input,
// and hide the ones not containing the input while showing the ones that do
$(list).find("a:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("a:Contains(" + filter + ")").parent().slideDown();
} else {
$(list).find("li").slideDown();
}
return false;
})
.keyup( function () {
// fire the above change event after every letter
$(this).change();
});
}
//ondomready
$(function () {
listFilter($("#friendssearchbox"), $("#friendslist"));
});
}(jQuery));
</script>
You'll need to do something similar to the code I have posted below. This assumes that you can access the object that contains the search results.
function toggle_visibility(id)
{
//Check if there are any search results to display
var searchResultLength = document.getElementById(searchResultID).innerHTML.length;
if (searchResultLength > 0) // display div
{
var e = document.getElementById(id);
e.style.display = 'block';
}
else //No search results, hide div
{
e.style.display = 'none';
}
}
Basically, you need to determine if you have search results to display before you attempt to toggle the div's visibility.
//EDIT AFTER COMMENTS
OK, so it looks like the results are adding li's to the ul. So, assuming that the code is taking away the li's as well as adding them, you should be checking for the number of elements in the ul == 0. See below.
$('#friendslist > li').length
To be honest, I'm having a bit of difficulty trying to determine exaclty what the code is
doing. I'm certainly not a jquery expert. I would say if the above code doesn't get you going in the right direction, I'm out of ideas.
If you're only wanting it to display when you enter the field use the onFocus="method()" attribute. followed by onBlur="method()". this will display the block when you enter the field and hide it when you leave.
<input id="searchbox" type="text" onFocus="toggle_visibility('friendslist');" onBlur="toggle_visibility('friendslist')">
<ul id="friendslist" style="display: none;">
<!--search results HTML-->
</ul>
teach a man to fish: http://www.w3schools.com/jsref/dom_obj_event.asp
// EDIT
I think Quickfire's answer is the best solution. but as I understand it you want you results to show/hide, so I modified his method to better suit your markup.
function toggle_visibility(id){
//Get the total number of <li> within my search result
var results=document.getElementById(searchResultID).childNodes.length;
if (results > 0){ // we have more than 0 results
var e = document.getElementById(id);
e.style.display = 'block'; // show the element
}else{ //No search results, hide div
e.style.display = 'none'; //hide the element
}
}