Getting my head around jQuery - javascript

OK, I'm designing a site and thought I'd stick some jQuery in as I really need so js experience.
Page with my problem is here: http://new.focalpix.co.uk/moreinfo.php
JS in question is:
$(document).ready(function(){
$(".answer").css("display","none");
$("#maincontent a.animate").click(function() {
$("#maincontent .answer").slideUp('slow');
var id = $(this).attr('href');
$(id).slideDown('slow');
return false;
});
});
This works fine, but if you click on a link where the answer has already slid down, then it slides up, then back down again.
I'm not sure on the cleanest way to stop this happening - any ideas?

You should be using the .slideToggle() effect.
$(document).ready(function() {
$(".answer").css("display","none");
$("#maincontent a.animate").click(function() {
$("#maincontent .answer").slideToggle('slow');
});
});

First, I'd suggest the following structure for your faq's:
<div id="faq">
<div class="qa" id="faq_greenandflies">
<span class="q">What is green and flies</span>
<div class="a">
Super Pickle!
</div>
</div>
<div class="qa" id="faq_redandbadforteeth">
<span class="q">What is Red and bad for your teeth</span>
<div class="a">
a Brick
</div>
</div>
<!--
More FAQ's here
-->
</div>
and then defining your jQuery as follows:
<script type="text/javascript">
$(function(){
// hide all answers
$('div#faq .qa .a').hide();
// bind a click event to all questions
$('div#faq .qa .q a').bind(
'click',
function(e){
// roll up all of the other answers (See Ex.1)
$(this).parents('.qa').siblings().children('.a').slideUp();
// reveal this answer (See Ex.2)
$(this).parents('.qa').children('.a').slideDown();
// return true to keep any other click events
return true;
});
// check location.hash to see if we need to expand one (direct link)
$(location.hash).find('.q a').click();
});
</script>
Explanation:
(Ex.1)
this is the link that was clicked
get the element that contains this and has a class of 'qa' (the box that contains both question and answer)
select all of its siblings. (we now have all qa's as a jQ object)
hide the answers
(Ex.2)
this is the line or link that was clicked
get the element that contains this and has a class of 'qa' (the box that contains both question and answer)
reveal the answer
A working demo is here.
This does several things for you:
If a user deep-links to an answer, the answer is automatically revealed
If a user clicks on one answer, all other answers are hidden
You can give your divs proper ids, so which helps search engine optimization of links to individual answers

Use slideToggle() like Soviut said, but just as a tip -- you can declare the display property in the actual CSS file instead of declaring it inside the javascript. jQuery will pick up on the fact that it is hidden in the stylesheet and still perform the appropriate slide function.
You can also use $(".answer").hide();
Instead of setting the display CSS property. Just thought I would let you know.

try using the one method, something like:
$(selector).one('effect', 'data for effect', callback function);
it makes sure an effect only happens once per element.

Related

Change elements/classes in/with jquery

I'm new in jQuery and used it right now for a navigation, that slides in and out in mobile or small views. That works fine and correct, but I'm using a plus-icon to open a submenu, that changes into a minus-icon, when the submenu is opened.
But it doesn't change back into the plus-icon, when the submenu is closed.
The code is the following:
$(document).ready(function() {
$('<span class="menu-expander"><span class="plusicon horizontal"></span><span class="plusicon vertical"></span></span>').insertAfter('.level_2');
$('#menu-toggle').click(function() {
$(this).next('#navigation-main').slideToggle();
});
$('.menu-expander').click(function() {
$(this).prev('.level_2').slideToggle();
$(this).children('span.plusicon.vertical').toggleClass('plusicon vertical');
});
});
I think the "interesting" part might be the second function, the first is still for a hamburger-icon, that opens the navigation, that works (okay, it doesn't show a sliding animation, what the second one do... no idea, why it don't works...).
So the second part is for the plus. When I click on the plus, the submenu slides in and the plus changes to the minus, but when I click back to the the minus it doesn't change back to the plus.
Has somebody any idea why it doesn't work or can explain me, how I can do it work?
Regards,
Markus
The problem is that your selector is trying to find a span with both plusicon and vertical classes but after the first call to this:
$(this).children('span.plusicon.vertical').toggleClass('plusicon vertical');
wich removes said classes, it is not able to find your target span.
To work around this you could assign an id (iconId on the next example) or another class to your icon so it can be allways found
$('<span class="menu-expander"><span id="iconId" class="plusicon horizontal"></span><span class="plusicon vertical"></span></span>').insertAfter('.level_2');
...
$('.menu-expander').click(function() {
$(this).prev('.level_2').slideToggle();
$(this).children('#iconId').toggleClass('plusicon vertical');
});
Do this :
$('.menu-expander').click(function() {
$(this).prev('.level_2').slideToggle();
var $icon = $(this).children('#ID OF ELEMENT'); // Would be easier to add an ID to your element whcih you want to alter - limits the error possibilties :)
if($icon.hasClass("CLASS YOU WANT TO GET RID OF"){
$icon.removeClass("CLASS YOU WANT TO GET RID OF");
$icon.addClass("THE CLASS YOU NEED");
else{
$icon.addClass("THE CLASS YOU WANT TO ADD");
}
});
I am at work now so pardon any typing errors.
You basically need to check whether the class that changes the icon to a MINUS symbol is still active - if so you change it back.
I hope it will help.
Points:
to find element good to use find();
better toggle 1 class to show hide element like "show" in example;
With elements inserted with js code better use .on() (for future);
$(document).ready(function() {
$('<span class="menu-expander"><span class="plusicon horizontal">horizontal</span><span class="plusicon vertical show">vertical</span></span>').insertAfter('.level_2');
$('#menu-toggle').click(function() {
$('#navigation-main').slideToggle();
});
$('.menu-expander').click(function() {
$(this).prev('.level_2').slideToggle();
$(this).find('.plusicon').toggleClass('show');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<style>
.plusicon {display:none}
.show {display:block!important}
</style>
<ul>
<li id="menu-toggle" class="level_2">Toggle</li>
</ul>
<ul id="navigation-main">
<li>test</li>
</ul>

jQuery won't hide form on click

I have three click events. Each one hides and shows different things depending on what is being clicked. Everything works except for the <form> with class markas doesn't get hidden when the <p> with class cancel is clicked.
Here is the jQuery:
$(".cancel").on("click", function(){
$(this).hide().parent().find('.celltext, .editlink, .marklink').show().parent().find('.editas, .markas').hide();
});
$(".editlink").on("click", function(){
$(this).hide().parent().find('.celltext, .marklink, .markas').hide().parent().find('.editas, .cancel').show();
$(this).parent().find(".editas input:text").focus();
});
$('.marklink').on("click", function(){
$(this).hide().parent().find('.celltext, .editlink, .editas').hide().parent().find('.markas, .cancel').show();
$(this).parent().find(".editas input:text").focus();
});
The HTML is exactly what you would assume. I have used a similar code with strings of .prev() and .next() and everything was hidden and shown correctly. Since I know it's not the internal HTML structure, rather than paste unnecessary code (I am using php functions and code to gather info from a database), the general structure is this:
<div>
<p class="celltext">Info from database</p>
<p class-"editlink">Edit</p>
<p class="marklink">Mark</p>
<form class="markas">Form with select box to mark the cell</form>
<form class="editas">Form with text fields to edit the info</form>
<p class="cancel">Cancel</p>
</div>
I have tried to hide the form in a separate line on its own, but that doesn't work either. Why doesn't it hide and how do I get it to hide properly when cancel is clicked?
UPDATE:
It has to do with the order that the forms display in. I swapped markas and editas and now markas hides and editas doesn't.
As per the comments above, this is the answer that solved the problem:
Without seeing a working example with the problem, I can't say for
sure, but I suspect you are picking up more elements than you intend
with the chained find() and parent() calls. I'd suggest doing a
parentEl = $(this).parent(); $(this).hide(); at the start of all of
your functions, then doing parentEl.find(...).show(); and
parentEl.find(...).hide(); It'll make your code easier to read, too.
$(".cancel").on("click", function(){
var parentel = $(this).parent();
$(this).hide();
parentel.find('.celltext, .editlink, .marklink').show();
parentel.find('.editas, .markas').hide();
});

JQuery Cookies with dynamic div ID's

I hope you guys can give me a push in the right direction as this problem has been eating me up all day now..
What I'm basicly trying to accomplish is this. I have several div's on a page that can be collapsed independently from eachother with the use of a button. Every div has it's own specific ID, generated with a string of static text, and a numeric value based on a auto-incremented database-value. This ensures I never have two div's with the same ID on one page. To target each specific div with Javascript (jQuery) I use the following code:
http://jsfiddle.net/LU7QA/0/
This works really well and does what it's supposed to do. Only there is one problem. On every page frefresh, every div that was opened is closed. Everything resets, and that's why I want to use JQuery Cookies in this construction. Only problem is, I know how it works, but I can't get it to work in this specific construction as it has to deal with a completely unique ID every time and needs to store the values of that particular ID.
As seen here: http://jsfiddle.net/LU7QA/1/
I tried to fiddle around with it but I can't seem to get it working properly and I'm starting to lose my sight on the problem..
<div>
<button class="button_slide" value="1">Show hide</button>
</div>
<div id="slidingDiv_1" class="slidingDiv">Stuff</div>
<div>
<button class="button_slide" value="2">Show hide</button>
</div>
<div id="slidingDiv_2" class="slidingDiv">Stuff</div>
function initMenu() {
$(".slidingDiv").hide();
// Toggle Field
$(".button_slide").click(function(){
//alert($(this).val()); debugging purposes
var sliding_id = $(this).val();
div_sliding_id = '#slidingDiv_'+sliding_id;
$(div_sliding_id).next().slideToggle('slow', function() {
$.cookie(div_sliding_id, $(this).is(':hidden') ? "closed" : "open");
return false;
});
});
$('.button_slide').each(function() {
var sliding_id = $(this).val();
div_sliding_id = '#slidingDiv_'+sliding_id;
if ($.cookie(div_sliding_id) == "open") $(this).next().show();
});
}
jQuery(document).ready(function() {initMenu();});
May you have missed a dot on the last *button_slide* declaration?
Btw, look at https://code.google.com/p/sessionstorage/

JS - Shouldn't this be written better? One function vs multiples?

I have a tweet stream where new tweets are added at the top and the older ones pushed down. You can click on the entire tweet and a panel slides down to reveal, "reply", "retweet", "favorite" etc. The panel is added to each new tweet added in the stream.
The code below works. Shouldn't this be better written so that only one call is being made? Or, as a new tweet is added. would I just have to add to the code with div#tc4, ul#tb4 etc?
$(document).ready(function () {
$("div#tc1").click(function () {
$("ul#tb1").slideToggle("fast");
});
$("div#tc2").click(function () {
$('ul#tb2').slideToggle("fast");
});
$("div#tc3").click(function () {
$('ul#tb3').slideToggle("fast");
});
});
Added Markup:
<div id="tc1" class="tweetcontainer">
<div class="avatarcontainer">
<div class="avatar"></div>
</div>
<div class="content">
<div class="tweetheader">
<div class="name">
<h1>John Drake</h1>
</div>
<div class="tweethandle">
<h2>#Drakejon</h2>
</div>
<div class="tweettime">10m</div>
</div>
<div>
<p>Exceptional Buys Ranger To Give Monitoring Shot In The Arm To Its 'DevOps' Platform http://tcrn.ch/11m3BrO by #sohear </p>
</div>
</div>
</div>
<!-------------Tool Bar -------------------------------->
<ul id="tb1" class="toolbar">
<li><a class="reply" href="#"><span>reply</span></a></li>
<li><a class="retweet" href="#"><span>retweet</span></a></li>
<li><a class="favorite" href="#"><span>favorite</span></a></li>
<li><a class="track" href="#"><span>track</span></a></li>
<li><a class="details" href="#"><span>details</span></a></li>
</ul>
I highly recommend separating your javascript from your detailed page function. The best way to do this is to put the retweeting panel inside the tweet container, then you don't even need to give it an id at all or encode in the javascript information about your html structure and ids. You can then just do:
$('.tweetcontainer').on('click', function(event) {
if ($(event.target).is(':descendantof(.toolbar)')) {
//ignore all clicks within the toolbar itself
return;
}
$(this).find('.toolbar').slideToggle();
});​
It's that easy! See it in action in a jsFiddle.
Now you can add as many tweet containers as you want to your page--and your javascript doesn't have to change one bit. Other solutions that require knowledge of specific ids linking to specific ids are suboptimal.
Note the descendantof pseudo-selector is custom (see the fiddle to find out how it works). Also, since you didn't provide any css, I had to choose some--it was quick so don't expect much. (Aww heck I just saw you updated your question to provide a jsFiddle with css giving a far prettier result--but I won't change mine now.) I did have to add a class to the actual tweet itself, but there is probably a better way to style it.
And if you want a click on the displayed toolbar itself (outside of a link) to allow collapsing the toolbar, change the code above to :descendantof(a).
If you don't want to change your page layout, another way to it is to encode the information about the linkage between html parts in the html itself using a data attribute. Change your tweetcontainer div to add a data attribute with a jQuery style selector in it that will properly locate the target:
<div class="tweetcontainer" data-target="#tb1">
You don't really have to remove the id if you use it elsewhere, but I wanted you to see that you don't need it any more. Then on document.ready:
$('.tweetcontainer').click(function () {
$($(this).data('target')).slideToggle('fast');
});
Here is another jsFiddle demonstrating this alternate technique (though it less elegant, in my opinion).
Last, I would like to mention that it seems possible you have a little bit of "div-itis". (We have all been there.) The toolbar anchor elements have unnecessary spans inside of them. The tweet name h1 element is inside a div, but could just be an h1 with class="name" instead.
In general, if there is only a single item inside a div and you can change your stylesheet to eliminate the div, then the div isn't needed. There are an awful lot of nested divs in your html, and I encourage you to remove as many of them as you can. Apply style to the other block elements you use and at least some, if not many, won't be needed.
I'd suggest (though currently untested):
$('div[id^="tc"]').click(function(){
var num = parseInt(this.id.replace(/\D+/g,''),10);
$('#tb' + num).slideToggle("fast");
});
Though given that you don't need the num to be a number (it'd be fine as a string), you could safely omit the parseInt().
Yes, you can write this code much more compactly like this:
$(document).ready(function() {
for (var i = 1; i < 3; i++) {
$("div#tc" + i).click(function() { $("ul#tb" + i).slideToggle("fast"); } );
}
});

How to make those dynamic anchor links with jQuery?

I've recently discovered a website which does something really cool! Example:
There are two links "Cat" and "Dog".
There are two DIV containers, id="catImage" and id="dogImage"
The initial URL looks like http://mysite.com/#cat
Initially, the cat image is visible
Clicking on the Dog link will fade out the catImage div and fade in the dogImage div
Additionally, it will change the anchor in the browser URL to: http://mysite.com/#dog
Opening the website with httü://mysite.com/#dog will show the dog image initially
Is this possible using jQuery? How would I scan for that anchor, and how would I call a function when the link is clicked without causing the link to follow some URL? I'm an objective-c dude and don't know anything about JS... hope my question isn't too dumb for you.
with this markup:
<div id="images">
<div id="catImage">some cat image here</div>
<div id="dogImage" style="display:none;">some dog image here</div>
</div>
<div id="anchors">
catImage anchor
dogImage anchor
</div>
and with this js (assuming jquery 1.4.x)
$(function () {
$("#anchors a").click(function () {
// send the index of the anchor to the function
fadeImage($(this).index());
});
var hash = window.location.hash;
if (hash) {
var elementId = "#" + hash.substring(1) + 'Image';
var $div = $(elementId);
// check if this element exists, and if so, send that index to the function
if ($div.length) {
fadeImage($div.index());
}
}
});
function fadeImage(index) {
$("#images div:eq(" + index + ")").fadeIn().siblings().fadeOut();
}
And to explain what's going on here:
I'm using the jquery index() function to get the index of the element relative to its siblings. I then pass that index to the fadeImage function, which finds the same index div and fades it in. By using chaining, I then look to the siblings of that div to fade them out. This is useful if you have more than 2 divs to fade out here.
For the anchor/hash usage, I just find the div with the matching id and get its index, and pass it to the same function.
jQuery docs can explain the various methods much better than I can.
Using location.href you can get the full URL in javascript.
you can substring or string replace your domain name and rest will be your parameter dog or cat.
When you have the parameter .
jquery functions like show(); hide (); to show cat and hide dog.
By adding or removing style also you can change images
.add() is there
addClass removeClass is there
http://mattwhite.me/11tmr.nsf/D6Plinks/MWHE-695L9Z
http://rockmanx.wordpress.com/2008/10/03/get-url-parameters-using-javascript/
http://api.jquery.com/show/
http://api.jquery.com/addClass/
Update: Oh I forgot, obviously you should read a tutorial about jQuery if you want to use it.
You can get and set the hash of the URL via window.location.hash, e.g.
alert(window.location.hash);
//and
window.location.hash = 'test'
You should read about events in order to fully understand how it works. The event object provides a preventDefault() method, which is exactly doing what it says (preventing the default behavior).
E.g. with jQuery:
$('a').click(function(e) {
//Do something..
// prevent to follow the link
e.preventDefault();
});

Categories