jQuery is not working the way it should and is completely ignoring the logic.
If I click a link, it shows up the given description, and fades the other menus.
If I click the same link again, it should hide that description, and fade the other links back in.
But instead it just hides the text, and doesn't fade them back in.
When running the code alone from the console and when you click on the whitespace next to the paragraphs, it works just fine.
Site for reference
jQuery:
$('a[class]').click(function(){
var clas = $(this).attr('class');
$('#'+clas.substring(0,2)).fadeTo('fast',1).removeClass('faded');
$('p:not(#'+clas.substring(0,2)+')').fadeTo('fast',0.3);
$('.ans:visible').toggle('slow');
$('#'+clas.substring(0,2)+'a'+':hidden').fadeIn('slow');
$('p:not(#'+clas.substring(0,2)+')').addClass('faded');
return false;
});
$('p:not(p.faded)').click(function(){
$('.ans:visible').toggle('slow');
$('p[class="faded"]').fadeTo('fast',1).removeClass('faded');
});
HTML:
<p id="q1">1. <a class="q1">Nem látom a kedvenc karakterem, hozzá tudod adni?</a>
<br>
<span id="q1a" style="display:none;" class="ans">
Persze. Írj egy e-mail-t a djdavid98#gmail.com címre a karakter nevével.
<br>
<span style="color:red">OC-kat és fillyket NEM adok hozzá.</span>
</span>
</p>
<p id="q2">2. <a class="q2">Hogyan tudok karaktert választani?</a>
<br>
<span id="q2a" style="display:none;" class="ans">
Látogass el a Karakterválasztás oldalra, ahol kiválaszthatod a kedvenced.
<br>
Haználhatod továbbá a "<i>Véletlenszerű karakter</i>" linket is.
</span>
</p>
<p id="q3">3. <a class="q3">Mi ennek az oldalnak a célja/alapötlete?</a>
<br>
<span id="q3a" style="display:none;" class="ans">
Eredetileg a milyennapvanma.hu weboldal pónisított változataként indult,
<br>
de azóta már nagy mértékben továbbfejlődött az oldal.
</span>
</p>
I admire your self-confidence: your code doesn't work so you assume the problem is with jQuery.
In your code, this statement:
$('p:not(p.faded)').click(function(){
...binds a click handler to any elements that don't have the "faded" class at that moment. Which would be all elements since none are faded initially. If you want it to apply only to elements that have not later had that class added you need to use a delegated handler which you assign via .on() (or .delegate() if using jQuery older than 1.7, or .live() if using a ridiculously old jQuery):
$(document).on('click', 'p:not(p.faded)'), function() {
Ideally you wouldn't bind the handler to document, you'd use the closest anscestor of the paragraphs in question, but since you haven't shown that much markup I'll leave that part to you.
Also though, you return false; from your click handler on the anchor elements, which prevents the click event from propagating up to the paragraphs anyway.
However, I think you're making the whole thing more complicated than you need to. The following code gets the job done:
var $questions = $('p'); // add class selectors here
$questions.click(function(){
var $this = $(this),
isOpen = $this.hasClass('open');
$this.fadeTo('fast',1).toggleClass('open',!isOpen)
.find('span.ans').toggle('slow');
$questions.not(this).fadeTo('fast', isOpen ? 1 : 0.2)
.removeClass('open')
.find('span.ans').hide('slow');
});
That is, when any paragraph is clicked, figure out whether it already has the answer open. Then make sure the clicked one is visible, and toggle its answer. Then take all of its sibling paragraphs and fade them in or out as appropriate and hide their answer.
Where I've put the comment "add class selectors here" it would be good to add a class to identify which paragraphs in your document are the questions.
Demo: http://jsfiddle.net/DxFDP/2
I would never use jQuery to apply styles to the code, but simple add and remove classes...
It will get messy, and sometime, we can simplify instead of complicate things.
here is simple example: http://jsbin.com/amiloc/1/
the same, but without <li>'s: http://jsbin.com/amiloc/3/
added colors so we know what's going on "under the hood", will let you judge by yourself.
Related
I have some radiobuttons and I have to click at the first one, but protractor keeps me returning "element not visible". Am I using the selector in a wrong way?
I've trying:
element.all(by.css('ui-radiobutton-icon.ui-clickable')).first.click();
and
element.all(by.class('ui-radiobutton-icon.ui-clickable')).first.click();
HTML
<p-dtradiobutton class="ng-star-inserted">
<div class="ui-radiobutton ui-widget">
<div class="ui-helper-hidden-accessible">
<input type="radio">
</div>
<div class="ui-radiobutton-box ui-widget ui-radiobutton-relative ui-state-default">
<span class="ui-radiobutton-icon ui-clickable"></span>
</div>
</div>
</p-dtradiobutton>
First of all, there is no such thing browser.class in protractor. Second thing is 2 mistakes in your code.
Instead of
element.all(by.css('ui-radiobutton-icon.ui-clickable')).first.click();
use this code
element.all(by.css('.ui-radiobutton-icon.ui-clickable')).first().click();
if you it errors out element not clickable or invisible
var EC = protractor.ExpectedConditions;
var element=element.all(by.css('.ui-radiobutton-icon.ui-clickable')).first();
var isClickable = EC.elementToBeClickable(element);
browser.wait(isClickable, 5000);
element.click();
Could you please try below.
element.all(by.css('ui-radiobutton-icon.ui-clickable input ')).first.click();
You're missing one "." before class ui-radiobutton-icon. Try this:
element.all(by.css('.ui-radiobutton-icon.ui-clickable')).first.click();
Few errors I see:
1) It is first() and not first
2) by.class('foo') should be by.className('foo')
3) not sure though, but you probably want to click the input element
In general when using locators, you should chain them. Try to begin at the very top of your page and chain it down to the element which should be selected. For reusability, you can also store chained element calls. In general, try to use element() instead of element.all() if possible.
For clicking the radio button I would suggest following code:
// start at the very top.
element(by.tagName('p-dtradiobutton'))
.element(by.css('.ui-radioButton .ui-helper-hidden-accessible'))
.element(by.tagName('input')).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();
});
I am building a very basic guessing game using jquery and html. I have 6 checkboxes, and if the right sequence is triggered, a hidden div appears. In order to trigger the div, I need to select 1,2,and 3. If you select 1,2,and 4, you get a secret message (div2), and otherwise nothing happens.
I can do the trigger easily by doing nested clicks:
$("#1").click(function(){
$("#2").click(function(){
$("#3").click(function(){
$("#div1").fadeIn();
});
});
});
html:
<input type="checkbox" id="#1">
<input type="checkbox" id="#2">
<input type="checkbox" id="#3">
<input type="checkbox" id="#4">
<input type="checkbox" id="#5">
<input type="checkbox" id="#6">
<div id="div1" style="width:30px;height:30px;display:none;background-color:blue;"></div>
<div id="div2" style="width:30px;height:30px;display:none;background-color:yellow;"></div>
But I am having trouble making it disappear.
If any one of the three is not pressed, I would like for that div to disappear. So let's say you press 1,2,3, div1 appears, and if you deselect 3, that div1 disappears.
I think I can make the question easier to phrase by phrasing it like this: i want to tell jquery- if one, and two, and three, are not 'all' selected, fade out the div.
Rather than using nested clicks, which will get complicated and confusing, you'd be better off creating a generalised listener that will maintain a list of what has/hasn't been clicked. Not only is this easier to maintain, it is also more optimal than having many click handlers assigned.
Others out there who may wish to optimise further may say correctly that you could write this code to directly generate a checked array, rather than a checked object, the reason I have kept it as an object is to support the possibility of a string-based ident rather than just numerical.
updated code
Previous code was slightly buggy, this version now works correctly when you select more checkboxes than you should.
reasons why
why change to use classes more than ids
Whilst ids are very specific, and will be more optimal for the browser to select by, they generally cause confusion and make things laborious, especially in markup that you wish to duplicate (obviously because ids have to be unique). It is often far better to come up with a solution that can work on a general grouping class, than having to label each element with a sequence i.e. cb1, cb2, cb3. As you can see my markup does label the checkboxes sequentially but the code only worries about the grouping class .cb, leaving the sequential classes really only for css styling.
why add a container div
When working on html5 apps, container divs will help you out 9 times out of 10. If you have a collection of elements that will only ever reside in a close visual formation, you will do yourself a favour by wrapping them. This helps when dynamically generating more elements (you can append your new elements directly to the container), it can help with delegating event listeners, and when targeting the elements via jQuery and CSS.
why use change instead of click for checkboxes
change is the event specifically designed to trigger when a change of value occurs, click is designed to fire when a click occurs. You should use the event that best suits what you want. In this case you only wish to update when a checkbox has changed it's value, which can happen with or without a mouse. True, some browsers fire the click event when using keyboard events, but it is better to be clear.
why use data-ident
ids should be used for quick look-up purposes, classes should be used to classify and group, if you have any other information to add to an element you should use the data- prefix. This means you aren't limited by what characters id and class support, and changing data- values doesn't cause any real internal calculations to fired by the browser i.e. applied classes or element registration.
how this code could be improved
The problem with making code more accessible and readable means that it's easier to work out what the code means, and this is bad for a game that should try and hide the solutions away from it's user-base. If this is just a simple game then there isn't much to worry about, but if you are working with something a bit more serious you should try and find a way to obfuscate the solutions :)
working fiddle:
http://jsfiddle.net/RFK92/
code:
/// your list of what is checked
var checked = {};
var updateDivs = function(){
var ident, show, checklist = [];
/// create a useful string from what has been checked
for ( ident in checked ) {
if ( checked[ident] ) {
checklist.push(ident);
}
}
checklist = checklist.join(',');
if ( checklist == '1,2,3' ) {
show = $('#div1');
}
else if ( checklist == '1,2,4' ) {
/// show something else, or not...
}
/// by using a grouping class you can find all divs that could be affected
$('.only-one-div').not(show).fadeOut();
if ( show ) {
/// and single one out for reveal
show.fadeIn();
}
};
$('.cb').change(function(){
var cb = $(this), ident = cb.data('ident');
/// keep track of what is or not checked
checked[ident] = cb.prop('checked'); /// updated to use prop!
/// update your divs
updateDivs();
});
markup:
<div class="cbs">
<input type="checkbox" class="cb cb1" data-ident="1" />
<input type="checkbox" class="cb cb2" data-ident="2" />
<input type="checkbox" class="cb cb3" data-ident="3" />
<input type="checkbox" class="cb cb4" data-ident="4" />
<input type="checkbox" class="cb cb5" data-ident="5" />
<input type="checkbox" class="cb cb6" data-ident="6" />
</div>
<div id="div1" class="only-one-div">one</div>
<div id="div2" class="only-one-div">two</div>
css:
.only-one-div { display: none; }
I would have a look at binding and unbinding your clicks.
Basically, if they have clicked the first proper click then bind the second proper click.
Any incorrect clicks would have you unbind all the clicks and fadeOut the divs and rebind the first necessary click.
http://api.jquery.com/bind/
http://api.jquery.com/unbind/
http://jsfiddle.net/CEb9x/1/
$('input[type=checkbox]').on('change', function(){
if($('input:checked').length == 3){
if ($('.blue:checked').length == 3) { $("#div1").fadeIn(); $("#div2").fadeOut();}
else if ($('input[name="secret"]:checked').length == 3) { $("#div1").fadeOut(); $("#div2").fadeIn(); }
} else { $("#div1, #div2").fadeOut(); }
});
This should do:
$("#1").click(function(){
checkboxClicked();
});
$("#2").click(function(){
checkboxClicked();
});
$("#3").click(function(){
checkboxClicked();
});
$("#4").click(function(){
checkboxClicked();
});
$("#5").click(function(){
checkboxClicked();
});
$("#6").click(function(){
checkboxClicked();
});
...
...
function checkboxClicked() {
if ($('#1').is(':checked') && $('#2').is(':checked') && $('#3').is(':checked') {
$('#div1').show();
$('#div2').hide();
}
else {
$('#div1').hide();
}
if ($('#1').is(':checked') && $('#2').is(':checked') && $('#4').is(':checked') {
$('#div2').show();
$('#div1').hide();
}
else {
$('#div2').hide();
}
}
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"); } );
}
});
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.