jQuery append seems to ghost append in an _.each statement - javascript

As strange as it sounds my code is simply this :
$element = "<div>Testing</div>";
_.each($('.chat-messages'), function(el) {
el.append($element);
debugger;
});
If I inspect from the debugger, when I type el in console, I can clearly see that $element.el was successfully appended. However, when I go and click continue, the element was never added.
The $('.chat-messages') selector returns two objects. The second was has $element.el added just fine to it. The first one doesn't. Even though the first one definitely returns the same kind of object as the second one. And in the debugger, I can clearly see that the object was appended to el. It just is never added to the actual HTML. Just the representational object within the debugger.
Am I just not selecting el correctly? How could the first and second one be different?

This works fine although I had to use timeout because the div wasn't rendering until then
Do you think you have a similar problem like the UI isn't rendered yet?
This code works if the $element as the OP originally asked is a string value "<div>Testing</div>"
var f = function() {
$element = "<div>Testing</div>";
_.each($('.chat-messages'), function(el) {
el.append($element);
//debugger;
});
};
setTimeout(f, 100);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='chat-messages'>
<div class='chat-messages'>
<div class='chat-messages'>
<div class='chat-messages'>
EDIT:
After going through the comments, I did a quick research of how the append works and what happens if we swap the $element with an actual $("<div>Testing</div>") only to find out that the comments were true
Note 1:
- The $.each is not required as you can choose all the elements matching the result and append at once
var f = function() {
$element = $("<div>Testing</div>");
$('.chat-messages').append($element);
};
setTimeout(f, 100);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='chat-messages'></div>
<div class='chat-messages'></div>
<div class='chat-messages'></div>
<div class='chat-messages'></div>
Now lets use $.each
var f = function() {
$element = $("<div>Testing</div>");
$('.chat-messages').each(function(ix, el) {
$(el).append($element);
});
};
setTimeout(f, 100);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='chat-messages'></div>
<div class='chat-messages'></div>
<div class='chat-messages'></div>
<div class='chat-messages'></div>
Note 2:
Use of each to iterate through the elements and appending will not yield the desired result as explained in the comments and also append documentation (pasting it below)
You can also select an element on the page and insert it into
another:
$( ".container" ).append( $( "h2" ) );
If an element selected this way is inserted into a single location
elsewhere in the DOM, it will be moved into the target (not cloned):

Related

Object returned by Jquery.find behaving unexpectedly with .data()

Take a look at this example :
let ele = $(`
<div></div>
<div class="test" id="test" data-test="test"></div>`
);
const ele1 = ele.find('.test'); //undefined
const ele2 = ele.find('#test'); //undefined
console.log(ele1.data());
console.log(ele2.data());
ele = $('<div class="test" id="test" data-test="test"></div>');
console.log(ele.data()); //works
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
There seems to be a difference between the object returned by .find() and the one returned by $()
I'm assuming it has something to do with the first ele being two siblings with no parent, but considering this can't be changed, how can I get #test's data?
I couldn't find anywhere what's the expected behaviour when you create an element with no parent like I did, and why does this happens.
Fiddle : https://jsfiddle.net/xpvt214o/26285/
You can just wrap that HTML inside a single div to be able to use find() on it. But, if you can't change the HTML, you can do it using filter(), since it's a collection of jQuery elements:
// Your element, as you defined it in your question
let ele = $(`
<div></div>
<div class="test" id="test" data-test="test"></div>`
);
console.log(ele.filter('.test').data());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Append additional html to cloned object in jquery

I want to add additional html in the cloned object.
var item = $("#clone")
.clone(true, true)
.attr({"id": "citem", "class": "row cartItem_" + item_id})
.css('display', 'block')
.appendTo("#all-items");
I know about wrap method but that is something else. I want to append html after this cloned object. Or somehow i can manipulate the HTML of the cloned object element.
This approach is to explain how the .clone() works, and covers all the states you ever mentioned in the question, such as..
Creating a clone of a DOM
Appending additional raw HTML to a clone
Manipulating the clone
Manipulating the content in the clone
Clone in another clone
Appending another clone to a clone
Appending HTML after this cloned object
$(function() {
//! Cloning the HTML
var $clonedContent = $('.content').clone();
// Manipulate the cloned element
// -- Update the existing content
$clonedContent.find('h5').text("My content just got manipulated");
// -- Adding raw HTML content
$clonedContent.append("<small> It's a raw HTML </small>");
// -- Adding property to an existing content
$clonedContent.find('small').addClass('make-me-day');
//! Getting another cloned content
var $anotherClonedContent = $('.content').clone();
// -- Another manipulation of another cloned content
$anotherClonedContent.find('h5').text("This is another cloned content");
// -- Manipulate the another cloned content's content
$anotherClonedContent.find('h5').addClass('make-me-day');
// -- Add another cloned content to the already manipulated & cloned content.
$clonedContent.append($anotherClonedContent);
//! Finally, add the clonedContent to the DOM, aaaand.. add more HTML afterwards.
$('#main').append($clonedContent, "<em> I added this HTML after the cloned object </em>");
});
.make-me-day {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
<div class="content">
<h5> Just a simple content </h5>
</div>
</div>
Assuming you are trying to add html after the clone:
$("#toclone")
.clone()
.attr({"id":"cloned"})
.appendTo("#all-items")
.after("<div>some more content <em>after</em> the clone</div>");
The .appendTo() returns the element that was appended, so you can then manipulate it as required, eg using .after()
I think that's more easy than you imagine:
$(function(){
var item_id=0;
// function to clone your element
var newItem=function(){
item_id++;
return $('#clone')
.clone(true, true)
.attr({'id':'citem_'+item_id, 'class':'row cartItem_'+item_id})
.css('display','block')
.appendTo('#all-items');
};
// Clone element and edit what you want
newItem().html('hobby').css('color','blue');
// Clone element and append what you want
newItem().append(' - <i>spaghetti</i>');
// You can also find element by id
$('#citem_2').css('color','red');
//You can add buttons to do it
$('button:eq(0)').on('click',function(){
newItem().html('Your <b>html</b> here.');
});
$('button:eq(1)').on('click',function(){
newItem().append(' - Your <b>html</b> here.');
});
});
<button>New clone</button>
<button>New clone + append</button>
<div id="all-items">
<div id="clone">pizza</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
I took a close look to all answers and comments to this bounty question...
I can see that the bidder is kind of demanding, which is okay since 100 rep. points is valuable.
I think that the question contains two, in fact.
How to clone
How to «manipulate the HTML of the cloned object» - Wasif Iqbal on Sep 22th.
I think the question is intended to get explanations on how to manipulate the clone, not only on creation and appending to somewhere, but also afterward.
I really think my very cool example below could be a «valid answer» - Vixed on Sep 29th.
The other answers were good too, anyway... So a made a supplemental effort. ;)
First explanation of all:
Cloning an element is done by jQuery .clone(). So have a nice reading.
Then:
jQuery chaining is nice to append some other stuff «inside» or «before/after» the clone in a concise way, as demonstrated in other answers.
But to manipulate it afterward, like in another click event handler...
This is the trick to know, which is not explained in the previous reference:
You have to make sure to set a unique id attribute to it, instead of the same id as the original.
Because you know that an id shall be unique!
«One ring to rule them all.
One ring to find them, one ring to bring them all and in the darkness bind them.»
- A well known deamon said this while forging a curse...
Then... What more explanation could I give if it ain't clear?
Alert reader should have understood everything already.
I made a funny «clone-and-kill-it-game» to demontrate cloning and further manipulations.
For the «inspiration», I have to admit that I saw a japaneese zombie movie yesterday night...
lol!
Have fun with this code snippet:
(also on CodePen)
// Constants
var number = 1;
var revealed = false;
// The cloning function
$("#cloneIt").click(function(){
var cloning = $("#Human")
.clone()
.attr({"id": "Clone_number_"+number, "class":"clone"})
.appendTo("#CloneBucket");
$(this).val("Clone him again! It's fun!");
number++;
if(number==4){
$(".reveal").show();
}
if(number==9){
$(this).val("Enought! This is now illegal.").prop("disabled",true);
}
// Add them to select
var options="<option disabled selected class='deceased'>KILL THEM!</option>";
for (i=1;i<number;i++){
if( $("#CloneBucket").children().eq(i-1).hasClass("dead") ){
options += "<option value='"+i+"' class='deceased'>Clone #"+i+"</option>";
}else{
options += "<option value='"+i+"'>Clone #"+i+"</option>";
}
}
$("#cloneSelect").html(options);
if(revealed){
reveal(); // Sub function to add clones to a select element.
}
});
// Reveal clone numbers
$(".reveal").click(function(){
reveal();
setTimeout(function(){
$(".reveal").val("Kill a clone! (While it's not illegal!)").removeClass("reveal").addClass("shoot");
},50);
});
// Kill button
$("#page").on("click",".shoot",function(){
$(this).prop("disabled",true).val("Select one");
$("#cloneSelect").show();
});
// Select kill target
$("#cloneSelect").change(function(){
var thisCloneIs = parseInt($(this).val());
var niceShot = "#Clone_number_"+thisCloneIs;
$(niceShot).css({"opacity":0.3,"color":"red"});
$(niceShot+" .definition").html("I was the number"+thisCloneIs).parent().addClass("dead");
// Redish the option
$(this).find("option").eq(thisCloneIs).prop("disabled",true).addClass("deceased");
$(this).find("option").eq(0).prop("selected",true);
// Bravo!
var allDead = [];
setTimeout(function(){
$("#cloneSelect").find("option").each(function(index){
if( $("#cloneSelect").find("option").eq(index).hasClass("deceased") ){
allDead.push(true);
}else{
allDead.push(false);
}
});
if( allDead.indexOf(false)==-1 ){
// Reset this super gaming experience for a new.
$("#CloneBucket").html("");
$(".shoot").addClass("reveal").removeClass("shoot").val("Reveal clone numbers!").prop("disabled",false).hide();
$("#cloneIt").val("Clone again?").prop("disabled",false);
$("#cloneSelect").html("").hide();
revealed = false;
number = 1;
}
},50);
});
function reveal(){
$(".clone .definition").each(function(index){
var cloneIndex = index+1; // zero-based
$(this).html("I'm the number "+cloneIndex);
revealed = true;
});
}
img{
width:60px;
}
div{
text-align:center;
}
.reveal{
display:none;
}
#CloneBucket div{
display:inline-block;
padding:10px;
}
#CloneBucket{
margin:0 auto;
text-align:center;
}
select{
display:none;
margin:0 auto;
}
.deceased{
color:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<div id="page">
<input type="button" id="cloneIt" value="Want to clone him?"><br>
<br>
<div id="Human">
<img src="http://image.flaticon.com/icons/svg/10/10522.svg"><br>
<span class="definition">I'm a real human!</span>
</div>
<br>
<input type="button" class="reveal" value="Reveal clone numbers!">
<select id="cloneSelect"></select>
<div id="CloneBucket"></div>
<br>
</div>
Still waiting for clarification in the comments, but I think this solution is what you are looking for:
$('button').click(function() {
$('#clone').clone()
.append('<span style="color:red;">some other elements</span>')
.removeAttr('id')
.appendTo('#all-items');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Click to clone</button>
<div id="all-items">
<div id="clone">pizza</div>
</div>
Since the appendTo returns the original element that was appended, you can use after on the returned value to add some new element after the cloned element that you just appended:
$('button').click(function() {
$('#clone').clone()
.append('<span style="color:red;">some other elements</span>')
.removeAttr('id')
.addClass('cloned')
.appendTo('#all-items')
.after('<div>this element was added after the cloned element (no blue border here)</div>');
});
.cloned {
border: 1px solid blue;
margin: 5px;
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Click to clone</button>
<div id="all-items">
<div id="clone">pizza</div>
</div>
One can add to a collection at any time using jQuery's add() function.
This effectively adds to the collection, placing whatever is passed to add() after the clone itself, as opposed to append which places the content inside the clone, answering the question
"I want to append html after this cloned object"
var more1 = $('<span />', {html : '<em> and will</em>'}); // element(s) to add
var more2 = '<span> happen again....</span>'; // or strings of HTML for that matter
var item = $("#clone").clone(true, true)
.attr({"id": "citem"})
.show()
.add(more1) // add whatever after the clone
.add(more2) // before appending everything
.appendTo("#all-items");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="clone">
<span>All of this has happened before</span>
</span>
<br /><br /><br />
<div id="all-items"><!-- clone goes here --></div>
From the documentation
Given a jQuery object that represents a set of DOM elements, the
.add() method constructs a new jQuery object from the union of those
elements and the ones passed into the method.
The argument to .add()
can be pretty much anything that $() accepts, including a jQuery
selector expression, references to DOM elements, or an HTML snippet.
example : $("p").clone().add("<span>Again</span>").appendTo(document.body);
add() does not change the original collection, but returns a new collection, so if not chaining directly on the modified collection, one has to store that collection
var clone = $('#elem').clone(true, true);
var changed = clone.add('<div>new content</div>'); // clone is not changed
Manipulating the content inside a clone is done in the exact same way as manipulating any other collection with jQuery
Post something link this
var clone = parent.find('.divclone').clone();
clone.removeClass('identifier');
clone.removeClass('hide');
//.. code changes for the new clone
clone.find(".link-slug").attr('href',invstr_val.slug_url);
// append it again to the original
clone.insertAfter(parent.find(".divclone"));

Loading content in a target div using jquery

So the first part of the code works fine as it should be, the two grid and list view loads in the views-div when clicked, however, I want a default view shown in the views-div using jquery, I tried loading using clone and prependto but it doesn't work. any suggestion on how to do this?
note: the content I'm loading from the backend has tags and ID's so if I use the html markup to show a default content in the views-div the content repeats. So I'm hoping if use jquery to load content, the repeating will not occur.
here's a demo http://jsfiddle.net/soulsurfer/eta0uyye/
$(document).ready(function() {
$('.iw-grid-select, .iw-list-select').on('click', function() {
var aID = $(this).attr('href');
var elem = $('' + aID).html();
$('#iw-grid-view').fadeOut("slow", 1000);
$('#iw-listview').fadeOut("slow", 1000);
$('#iw-views-div').html(elem);
});
$( "#iw-grid-view" ).contents().find( ".iw-grid-list-col" ).clone().prependTo("#iw-views-div");
});
The simplest solution could be is to trigger a click event
$(document).ready(function() {
$('.iw-grid-select, .iw-list-select').on('click.view', function() {
var aID = $(this).attr('href');
var elem = $('' + aID).html();
$('#iw-grid-view').fadeOut("slow", 1000);
$('#iw-listview').fadeOut("slow", 1000);
$('#iw-views-div').html(elem);
}).first().trigger('click.view');
});
.iw-listview,
.iw-grid-view {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="iw-filter-col ">
grid view link
list view link
</div>
<div class="row iw-listing-view-row">
<div class="iw-grid-view" id="iw-grid-view">Grid view content</div>
<div id="iw-listview" class="iw-listview">list view content</div>
<div class="iw-views-div" id="iw-views-div">Content loading column</div>
<div id="loading"></div>
</div>
I know that this has been resolved, but just in case anyone was wondering where OP went wrong, I have a theory.
This:
$( "#iw-grid-view" ).contents().find( ".iw-grid-list-col" ).clone().prependTo("#iw-views-div");
Should be changed to this:
$( "#iw-grid-view" ).find( ".iw-grid-list-col" ).clone().prependTo("#iw-views-div");
If the .iw-grid-list-col element was an immediate child of #iw-grid-view, then find() wouldn't have found it when called on the return value of contents(). This is because find() searches through descendants of elements. The return value of contents(), in this case, would have included the .iw-grid-list-col element and find() would not have found it since it was a member of the array that find() was called on, rather than a descendant of a member of the array.
Removing contents() from that chain of function calls allows find() to search all of the descendants of #iw-grid-view instead of just the descendants of its immediate children.

How to get sibling element (div) in Javascript?

So I have this HTML:
<div class="tip-box">
<div class="tip-title" onclick="toggleTip()">
<h2>Tip 1</h2>
</div>
<div class="tip-content hidden">
<p>Tip 1 content</p>
</div>
</div>
And this Javascript:
function toggleTip() {
$(this).siblings(".tip-content").toggleClass("hidden");
}
Hopefully it's obvious what this is supposed to do, but it doesn't work. Using .siblings() just doesn't seem to work in this way.
What's the correct solution for this? To get the next sibling of a certain type or with a certain class and then hide/show it?
You can use Jquery function.
<div class="tip-box">
<div class="tip-title">
<h2>Tip 1</h2>
</div>
<div class="tip-content hidden">
<p>Tip 1 content</p>
</div>
</div>
$(document).ready(function(){
$('.tip-title').click(function(){
$(this).siblings(".tip-content").toggleClass("hidden");
});
});
you can also use this
<div class="tip-box">
<div class="tip-title" onclick="toggloTip(this)">
<h2>Tip 1</h2>
</div>
<div class="tip-content hidden">
<p>Tip 1 content</p>
</div>
</div>
<script>
function toggloTip(elm) {
$(elm).siblings(".tip-content").toggleClass("hidden");
}
</script>
You can use pure javaScript with nextElementSibling property of node something like below,
I suppose you want do this operation with siblings.
function getChildrens(n, selector) {
var nodes = [];
while (n.nextElementSibling != null) {
if (n.nextElementSibling.hasOwnProperty('classList')) {
if (n.nextElementSibling.classList.contains(selector)) {
//return n.nextElementSibling;
nodes.push(n.nextElementSibling);
}
}
n = n.nextElementSibling;
}
return nodes;
};
function getSiblings(n, selector) {
return getChildrens(n, selector);
}
function toggleTip(elem) {
var siblings = getSiblings(elem, "tip-content");
if (siblings.length > 0) {
for (var i = 0; i < siblings.length; i++) {
siblings[i].classList.toggle("hidden");
}
}
}
.hidden {
display: none;
}
<div class="tip-box">
<div class="tip-title" onclick="toggleTip(this)">
<h2>Tip 1</h2>
</div>
<div class="tip-content hidden">
<p>Tip 1 content</p>
</div>
</div>
Here is another non JQuery answer.
To get the next element sibling use:
var nextElement = element.nextElementSibling;
To get the previous element sibling use:
var previousElement = element.previousElementSibling;
To get the element index use:
var index = Array.prototype.slice.call(element.parentElement.children).indexOf(element);
If you are at the first element the previousElementSibling value will be null.
If you are at the last element the nextElementSibling value will be null.
How about this JavaScript:
$(function(){
$('.tip-box').on('click', '.tip-title', function(){
$(this).next('.tip-content').toggleClass('hidden');
});
});
Remove the idea of working with onclick attributes when you use jQuery.
None of the previous answers, not even that serial-upvoted one ;), actually explains the problem and why their solutions work.
The problem is that an inline onclick handler does not pass on its current context. Inside the onclick="" JavaScript code this is the element clicked. Once you call a global function (like your toggleTip), that context is lost. The this the function receives is window and not the element.
The usual quick fix, for raw JavaScript code, is to pass this as a parameter to the global function.
e.g.
onclick="toggleTip(this)"
and receive a parameter in the function like this:
function toggleTip(element) {
$(element).siblings(".tip-content").toggleClass("hidden");
}
However, as you are using jQuery, inline event handlers are actually a bad idea. They separate the event registration from the event handler code for no reason and do not allow for multiple event handlers, of the same type, on the same element. They also bypass the rather cool event bubbling system jQuery uses.
The preferred alternative, with jQuery, is to use jQuery to select the element and jQuery to connect the event in one step:
jQuery(function($){
$('.tip-title').click(function(){
$(this).siblings(".tip-content").toggleClass("hidden");
});
});
As you only want the element that follows, and potentially will add more pairs, the better option would be using nextAll and first(), with the same jQuery filter, instead of siblings:
e.g.
jQuery(function($){
$('.tip-title').click(function(){
$(this).nextAll(".tip-content").first().toggleClass("hidden");
});
});
Or, of you can guarantee it is the next element, use next as #Tim Vermaelen did (with or without the selector makes no difference, so might as well leave it out):
jQuery(function($){
$('.tip-title').click(function(){
$(this).next().toggleClass("hidden");
});
});
Note: In this example jQuery(function($){ is a DOM ready event handler which is the rather handy shortcut version of $(document).ready(function(){YOUR CODE});, which also passes a locally scoped $ value. For those that mistake this code for an incorrect IIFE, here is a working example: http://jsfiddle.net/TrueBlueAussie/az4r27uz/

How to Reduce Size of This jQuery Script and Make it More Flexible?

I just created script that shows/hides (toggles) block of HTML. There are four buttons that each can toggle its HTML block. When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button... it hides that HTML block and shows new one.
Here is what I have at the moment:
$('.btn_add_event').click( function() {
$('.block_link, .block_photos, .block_videos').hide();
$('.block_event').toggle();
});
$('.btn_add_link').click( function() {
$('.block_event, .block_photos, .block_videos').hide();
$('.block_link').toggle();
});
$('.btn_add_photos').click( function() {
$('.block_event, .block_link, .block_videos').hide();
$('.block_photos').toggle();
});
$('.btn_add_videos').click( function() {
$('.block_event, .block_link, .block_photos').hide();
$('.block_videos').toggle();
});
Any ideas how to reduce code size? Also, this script isn't very flexible. Imagine to add two new buttons and blocks.
like Sam said, I would use a class that all the blocks share, so you never have to alter that code. Secondly, you can try 'traversing' to the closest block, therefore avoiding it's name. That approach is better than hard coding each specific block, but if the html dom tree changes you will need to refactor. Last, but best, you can pass in the class name desired block as a variable to the function. Below is something you can copy paste that is close to what you started with.
$('.myAddButtonClass').click( function() {
$('.mySharedBlockClass').filter(':visible').hide();
//find a good way to 'traverse' to your desired block, or name it specifically for now.
//$(this).closest(".mySharedBlockClass").show() complete guess
$('.specificBlockClass').show();
});
I kept reading this "When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button" thinking that my eyes were failing me when Its just bad English.
If you want to make it more dynamic, what you can do is add a common class keyword. Then
when the click event is raise. You can have it loop though all the classes that have the
keyword and have it hide them all (except the current one that was clicked) and then show the current one by using the 'this' keyword.
you can refer below link,
http://chandreshmaheshwari.wordpress.com/2011/05/24/show-hide-div-content-using-jquery/
call function showSlidingDiv() onclick event and pass your button class dynamically.
This may be useful.
Thanks.
try this
$('input[type=button]').click( function() {
$('div[class^=block]').hide(); // I resumed html block is div
$(this).toggle();
});
Unfortunatly I couldn't test it, but if I can remember right following should work:
function toogleFunc(clickObject, toogleTarget, hideTarget)
{
$(clickObject).click(function()
{
$(hideTarget).hide();
$(toogleTarget).toggle();
});
}
And the call:
toogleFunc(
".btn_add_videos",
".block_videos",
".block_event, .block_link, .block_photos"
);
and so far
Assuming the buttons will only have one class each, something like this ought to work.
var classNames = [ 'btn_add_event', 'block_link', 'block_photos', 'block_videos' ];
var all = '.' + classNames.join(', .'); // generate a jquery format string for selection
$(all).click( function() {
var j = classNames.length;
while(j--){
if( this.className === classNames[j] ){
var others = classNames.splice(j, 1); // should leave all classes but the one on this button
$('.' + others.join(', .')).hide();
$('.' + classNames[j]).toggle();
}
}
}
All the buttons have the same handler. When the handler fires, it checks the sender for one of the classes in the list. If a class is found, it generates a jquery selection string from the remaining classes and hides them, and toggles the one found. You may have to do some checking to make sure the strings are generating correctly.
It depends by how your HTML is structured.
Supposing you've something like this
<div class="area">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
...
<div class="sender">
<a class="one"></a>
<a class="two"></a>
<a class="three"></a>
</div>
You have a class shared by the sender and the target.
Your js would be like this:
$('.sender > a').click(function() {
var target = $(this).attr('class');
$('.area > .' + target).show().siblings().hide();
});
You show your real target and hide its siblings, which aren't needed.
If you put the class postfixes in an array, you can easily make this code more dynamic. This code assumed that it doesn't matter in which order toggle or hide are called. If it does matter, you can just remember the right classname inside the (inner) loop, and toggle that class after the loop.
The advantage to this approach is that you can extend the array with an exta class without needing to modifying the rest of the code.
var classes = new Array('videos', 'event', 'link', 'photos');
for (var i = 0; i < classes.length; ++i)
{
$('.btn_add_' + classes[i]).click(
function()
{
for (var j = 0; j < classes.length; ++j)
{
if (this.hasClass('btn_add_' + classes[j]))
{
$('.block_' + classes[j]).toggle();
}
else
{
$('.block_' + classes[j]).hide();
}
}
});
}
You could make this code more elegant by not assigning those elements classes like btn_add_event, but give them two classes: btn_add and event, or even resort to giving them id's. My solution is based on your description of your current html.
Here is what I think is a nice flexible and performant function. It assumes you can contain your links and html blocks in a parent, but otherwise it uses closures to precalculate the elements involved, so a click is super-fast.
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" ></script>
<script type="text/javascript">
// Enables show/hide functionality on click.
// The elements within 'container' matching the selector 'blocks' are hidden
// When elements within 'container' matching the selector 'clicker' are clicked
// their attribute with the name 'clickerAttr' is appended to the selector
// 'subject' to identify a target, usually one of the 'blocks'. All blocks
// except the target are hidden. The target is shown.
//
// Change clickerAttr from 'linkTarget' to 'id' if you want XHTML compliance
//
// container: grouping of related elements for which to enable this functionality
// clicker: selector to element type that when clicked triggers the show/hide functionality
// clickerAttr: name of the DOM attribute that will be used to adapt the 'subject' selector
// blocks: selector to the html blocks that will be shown or hidden when the clicker is clicked
// subject: root of the selector to be used to identify the one html block to be shown
//
function initToggle(container,clicker,clickerAttr,blocks,subject) {
$(container).each(
function(idx,instance) {
var containerElement = $(instance);
var containedBlocks = containerElement.find(blocks);
containerElement.find(clicker).each(function(idxC, instanceClicker) {
var tgtE = containerElement.find(subject+instanceClicker.getAttribute(clickerAttr));
var clickerBlocks = containedBlocks.not(tgtE);
$(instanceClicker).click(function(event) {
clickerBlocks.hide();
tgtE.toggle();
});
});
// initially cleared
containedBlocks.hide();
}
);
}
$(function() {
initToggle('.toggle','a.link','linkTarget','div.block','div.');
});
</script>
</head>
<body>
Example HTML block toggle:
<div class="toggle">
a <br />
b <br />
c <br />
<div class="A block"> A </div>
<div class="B block"> B </div>
<div class="C block"> C </div>
</div> <!-- toggle -->
This next one is not enabled, to show scoping.
<div class="toggle2">
a <br />
<div class="A block">A</div>
</div> <!-- toggle2 -->
This next one is enabled, to show use in multiple positions on a page, such as in a portlet library.
<div class="toggle">
a <br />
<div class="A block">A</div>
</div> <!-- toggle (2) -->
</body>
</html>

Categories