How to click a button until the button isn't visible? - javascript

I'm trying to write a step that clicks a more button until that button changes to display: none;.
CLARIFICATION: The page I'm working with is a Google+ business page. There is a more button that loads about 10 reviews at a time until they are all loaded. Once they are, Google sets the button to display: none;
I've played around with a ton of different ways to accomplish it. Right now I'm working with something like:
This one is not working, and I'm sure there is an elegant way to accomplish this.
casper.then(function() {
while (this.visible('.d-s.L5.r0')){
console.log("Click");
this.click('.d-s.L5.r0');
}
});
This new code is working, but I have to set how many times it repeats which is pretty hacky:
casper.repeat(15, function() {
console.log("Click");
this.click('.d-s.L5.r0');
this.wait(400);
});

You need proper status handling of the button. By clicking the more button it will be invisible, but another loading span will be visible until the next items are loaded. Then it is exchanged again. You need to reflect the change in your code.
if (this.visible(moreButton)) { // synchronous
// asynchronous steps...
this.thenClick(moreButton);
this.waitUntilVisible(loadingButton);
this.waitUntilVisible(moreButton);
this.then(function(){
this.capture("business.png"); // overwrite the current screenshot
// recursion here
});
}
Additionally, it is good practice to write the thing recursive, because you don't know the number of steps until you try the steps. So the complete code would be:
var casper = require('casper').create({
waitTimeout: 10000,
viewportSize: {
width: 900,
height: 720
}
});
var moreButton = '[id*="about-page"] span.d-s.L5.r0',
loadingButton = moreButton + ' + span.dP.PA';
function clickMore(max, i){
i = i || 0;
if ((max == null || i < max) && this.visible(moreButton)) { // synchronous
// asynchronous steps...
this.thenClick(moreButton); // sometimes the click is not properly dispatched
this.waitUntilVisible(loadingButton);
this.waitUntilVisible(moreButton, null, function onTimeout(){
// only placeholder so that the script doesn't "die" here if the end is reached
});
this.then(function(){
this.capture("business_"+i+".png");
clickMore.call(this, max, i+1); // recursion
});
}
}
casper.start("https://plus.google.com/101200069268247335090/about?hl=en").then(function(){
clickMore.call(casper);
}).then(function(){
this.capture("business_end.png");
}).run(function(){
this.echo("DONE");
this.exit();
});
You can also call clickMore with an argument setting the maximum number of clicks like this:
clickMore.call(casper, 10);
A problem remains. The click on the more button is sometimes not dispatched. You should explore other approaches clicking that button.

Related

Function with attr() function not executing all statements when there are multiple elements on screen created dynamically by a function

The function below creates a red dot on the right end of the screen, animate() to move it left and when clicked, its src file should be changed, along with a few other things as you can see below:
function spawnEnemy()
{
$("body").prepend("<img src='red_dot.png' class='enemies' id='enemy' style='left:calc(" + thewidth/2 + "px)'/>");
$("#enemy").attr("id", "enemy"+i);
$("#enemy"+i).css({"left" : thewidth+'px', "top" : (randomInt(10,500))+'px'});
$("#enemy"+i).animate({left :'-400px'}, 20000);
$("#enemy"+i).click(function()
{
var snd = new Audio("laser.mp3");
snd.play(); //function stops here
$("#enemy"+i).stop();
$("#enemy"+i).attr("src","explosion.gif");
$("#enemy"+i).css({"transform" : "scale(0.11,0.11)"});
setTimeout(function()
{
$('#enemy'+i).hide();
}, 350);
});
i+=1;
}
This function is executed every six seconds. If, however, the element is not clicked before a new element is created, then all of the elements when clicked stop executing at the line where I have placed a comment. I checked the console, and I couldn't find anything of value, although I know that the problem isn't with the ids as they all have different ids in the console, and since I can hear the sound from snd.play() and none of the effects of the statements after snd.play() are seen, I'm sure the problem lies there. This problem is only when multiple elements are created, as they all execute fine if I click the element before six seconds, i.e, before the function is executed again. Also, thewidth is just a pre-defined global var, so it's not related.
I'm very new to jquery, so I apologize if there's something obvious I'm missing.
Edit: Okay, now all the statements aren't executing even if there is only one element on the screen. I'm quite lost.
Ok, I think I see what's happening. You need to reference enemy with $(this) inside the callback, because i is in the encapsulating scope, which means it can change and break the internal scope of that callback:
While at it, I cleaned up your jQuery code a little to simplify it.
function spawnEnemy() {
$('<img />', {
id: 'enemy' + i,
src: 'red_dot.png',
className: 'enemies',
style: 'left: ' + thewidth / 2 + 'px; top: ' + randomInt(10,500) + 'px'
})
.prependTo($('body'));
.animate({left :'-400px'}, 20000);
.click(function() {
var snd = new Audio("laser.mp3");
snd.play(); //function stops here
$(this).stop();
$(this).attr("src","explosion.gif");
$(this).css({"transform" : "scale(0.11,0.11)"});
setTimeout(function() {
$(this).hide();
}.bind(this), 350);
});
i+=1;
}

Using a jquery slider for text instead of images?

This may be a little too specific, but I have a jquery slider that I am using <p> classes instead of images to cycle through customer quotes. Basically the problem I am running into right now is when it is static and non moving (JS code is commeneted out) they are aligned how I want them to be. As soon as the JS is un commented, they stretch out of view and you just see a white box?
Any ideas?
How I want each panel to look like:
jsfiddle
So I sort of made this my Friday project. I've changed a whole lot of your code, and added a vertical-align to the quotes and authors.
Here's the fiddle http://jsfiddle.net/qLca2fz4/49/
I added a whole lot of variables to the top of the script so you could less typing throughout.
$(document).ready(function () {
//rotation speed and timer
var speed = 5000;
var run = setInterval(rotate, speed);
var slides = $('.slide');
var container = $('#slides ul');
var elm = container.find(':first-child').prop("tagName");
var item_width = container.width();
var previous = 'prev'; //id of previous button
var next = 'next'; //id of next button
Since you used a % based width I'm setting the pixel widths of the elements in case the screen is reszed
slides.width(item_width); //set the slides to the correct pixel width
container.parent().width(item_width);
container.width(slides.length * item_width); //set the slides container to the correct total width
As you had, I'm rearranging the slides in the event the back button is pressed
container.find(elm + ':first').before(container.find(elm + ':last'));
resetSlides();
I combined the prev and next click events into a single function. It checks for the ID of the element targeted in the click event, then runs the proper previous or next functions. If you reset the setInterval after the click event your browser has trouble stopping it on hover.
//if user clicked on prev button
$('#buttons a').click(function (e) {
//slide the item
if (container.is(':animated')) {
return false;
}
if (e.target.id == previous) {
container.stop().animate({
'left': 0
}, 1500, function () {
container.find(elm + ':first').before(container.find(elm + ':last'));
resetSlides();
});
}
if (e.target.id == next) {
container.stop().animate({
'left': item_width * -2
}, 1500, function () {
container.find(elm + ':last').after(container.find(elm + ':first'));
resetSlides();
});
}
//cancel the link behavior
return false;
});
I've found mouseenter and mouseleave to be a little more reliable than hover.
//if mouse hover, pause the auto rotation, otherwise rotate it
container.parent().mouseenter(function () {
clearInterval(run);
}).mouseleave(function () {
run = setInterval(rotate, speed);
});
I broke this in to its own function because it gets called in a number of different places.
function resetSlides() {
//and adjust the container so current is in the frame
container.css({
'left': -1 * item_width
});
}
});
//a simple function to click next link
//a timer will call this function, and the rotation will begin :)
And here's your rotation timer.
function rotate() {
$('#next').click();
}
It took me a little bit, but I think I figured out a few things.
http://jsfiddle.net/qLca2fz4/28/
First off, your console was throwing a few errors: first, that rotate wasn't defined and that an arrow gif didn't exist. Arrow gif was probably something you have stored locally, but I changed the 'rotate' error by changing the strings in the code here to your actual variables.
So, from:
run = setInterval('rotate()', speed);
We get:
run = setInterval(rotate, speed);
(No () based on the examples here: http://www.w3schools.com/jsref/met_win_setinterval.asp)
But I think a more important question is why your text wasn't showing up at all. It's because of the logic found here:
$('#slides ul').css({'left' : left_value});
You even say that this is setting the default placement for the code. But it isn't..."left_vaule" is the amount that you've calculated to push left during a slide. So if you inspect the element, you can see how the whole UL is basically shifted one slide's worth too far left, unable to be seen. So we get rid of 'left_value', and replace it with 0.
$('#slides ul').css({'left' : 0});
Now, there's nothing really handling how the pictures slide in, so that part's still rough, but this should be enough to start on.
Let me know if I misunderstood anything, or if you have any questions.
So, a few things:
1) I believe you are trying to get all of the lis to be side-by-side, not arranged up and down. There are a few ways to do this. I'd just make the ul have a width of 300%, and then make the lis each take up a third of that:
#slides ul {
....
width: 300%;
}
#slides li {
width: calc(100% / 3);
height:250px;
float:left;
}
2) You got this right, but JSFiddle automatically wraps all your JS inside a $(document).ready() handler, and your function, rotate needs to be outside, in the normal DOM. Just change that JSFiddle setting from 'onload' to 'no wrap - in head'
3) Grabbing the CSS value of an element doesn't always work, especially when you're dealing with animating elements. You already know the width of the li elements with your item_width variable. I'd just use that and change your code:
var left_indent = parseInt($('#slides ul').css('left')) - item_width;
$('#slides ul').animate({'left' : left_indent}, 1500, function () {
to:
$('#slides ul').stop().animate({'left' : -item_width * 2}, 1500, function () {
4) Throw in the .stop() as seen in the above line. This prevents your animations from overlapping. An alternative, and perhaps cleaner way to do this, would be to simply return false at the beginning of your 'next' and 'prev' functions if #slides ul is being animated, like so:
if ($('#slides ul').is(':animated')) return false;
And I think that's everything. Here's the JSFiddle. Cheers!
EDIT:
Oh, and you may also want to clearInterval at the beginning of the next and prev functions and then reset it in the animation callback functions:
$('#prev').click(function() {
if ($('#slides ul').is(':animated')) return false;
clearInterval(run);
$('#slides ul').stop().animate({'left' : 0}, 1500,function(){
....
run = setInterval('rotate()', speed);
});
});

How to fadeIn element on page load instead of "appear"?

Im a really huge noob on jquery, I need to figure out how to change this code:
$('.social li').appear();
$(document.body).on('appear', '.social li', function(e, $affected) {
var fadeDelayAttr;
var fadeDelay;
$(this).each(function(){
if ($(this).data("delay")) {
fadeDelayAttr = $(this).data("delay")
fadeDelay = fadeDelayAttr;
} else {
fadeDelay = 0;
}
$(this).delay(fadeDelay).queue(function(){
$(this).addClass('animated').clearQueue();
});
})
});
to work in the way that it would start animation as soon as someone enters the landing page, right now it works good on everything besides IE10 and IE11, was told to change it to load by default not on "appear" but I tried document ready/load and I can't get it to work...
You could try fading all list items into view, each with a progessing 250ms delay:
$(window).load(function() {
$('.social li').hide().each(function(i) {
$(this).delay((i + 1) * 250).fadeIn(2000);
});
});
EDIT:
Using the same logic as your previous code to refactor, use the window.load method since the load event fires at the end of the document loading process. At this point, all of the objects in the document are in the DOM, and all the images and sub-frames etc have finished loading. So use this event to do the fading in animation of the list items into view, where their initial state will be hidden.
You have two variables declared fadeDelayAttr and fadeDelay but I noticed that only fadeDelay is being used, so fadeDelayAttr can be discarded. Also, this part of the code:
if ($(this).data("delay")) {
fadeDelayAttr = $(this).data("delay")
fadeDelay = fadeDelayAttr;
} else {
fadeDelay = 0;
}
can be simplified as the null-coalescing operator using a logical OR (||):
var fadeDelay = $(this).data("delay") || 0;
Since the fadeDelay variable gets its value from the data-delay attribute, this can then be passed in as an argument for the delay method and finally the refactored code will look like this:
$(window).load(function() {
$('.social li').hide().each(function() {
var fadeDelay = $(this).data("delay") || 0;
$(this).delay(fadeDelay).fadeIn(2000);
});
});
Working Demo

jQuery wait for an action to complete on different functions

I have created a newsfeed. The feed switches every 2 seconds. You can also manually switch left/right, or click the panel from the squares at the bottom. The switching between slides is down using jQuery UI Slide.
Right now, if you are in the middle of a slide, and you click left/right/squares, then another slide occurs on top of the existing, still going slide and the whole system is messed up.
How can I prevent other actions occurring if a slide/switch is already in progress?
This is my code:
$(document).ready(function(){
newsfeedTimer = setInterval(newsfeed, displayDuration);
// Manual change of feed (LEFT)
$('#newsfeeds_wrapper > .left').click(function(event){
event.stopPropagation();
feedLeft();
clearInterval(newsfeedTimer);
newsfeedTimer = setInterval(newsfeed, displayDuration);
});
// Very similar code for feed right
// Ignore the other method of switching (if it works for above, I can implement it for this one)
});
function newsfeed() {
feedRight();
}
// Feed to the Right
// jump is used to jump multiple newsfeed instead of one at a time
function feedRight(jump)
{
jump = typeof jump !== 'undefined' ? jump : 1;
var current = $('.newsfeed:first');
var next = $('.newsfeed:nth(' + jump + ')');
current.hide('slide',{duration: transitionDuration}, function(){
// Append as many needed
for( var i = 0; i < jump; i++ ) {
$('.newsfeed:first').appendTo('#newsfeeds');
}
next.show('slide',{direction : 'right' , duration: transitionDuration});
}
I don't want to stop() an animation! I want to disable changing the slides IF there is animation happening!!
without seeing the full breadth of the code, I am shooting myself in the foot here. But here is a direction I would take it. You could also have two functions, one to bind, another to unbind. When animation is initiated, you unbind the left/right controls. When stopped, you bind. Or, set a global variable... ala.
var config = {'inProgress': false};
$('#newsfeeds_wrapper > .left').click(function(event){
if(!config.inProgress){
event.stopPropagation();
feedLeft();
clearInterval(newsfeedTimer);
newsfeedTimer = setInterval(newsfeed, displayDuration);
}
});
in your animation function. Seems like when you cut/paste, some of the code is lost, so lets just assume some animation.
when you enter your animation functions, set config.inProgress = true;
function feedRight(jump)
{
config.inProgress = true;
// removed your code, but just using for simplicity sake
// added a callback
next.show('slide',{direction : 'right' , duration: transitionDuration},
function() {
// Animation complete. Set inProgress to false
config.inProgress = false;
});
)
}

Having issue with jquery animation effects

Live Example Here
Press any of buttons. Can you see shaky slideDown effect? In fact it must be smooth and slow slide down.
If you've pressed for 1 time any of buttons try to press another one. The show and hide animation of forms is awful.
During anim.
After anim.
How to fix this problems?
The shakiness seems to be happening when each of the elements in the tab is hidden or displayed.
Get the height of each of your tabs and implement your resizing animation to those heights rather than individually animating after each element is hidden or displayed.
Try to play with .animate() and .fadeIn() .fadeOut() .fadeTo(time,opacity) instead of just .show() or .hide()
It's loading quite well for me. However it did take a long time for it to load in the first place. If you go to: http://gtmetrix.com/reports/tural.no-ip.org/ANBNA45n You'll see that your page is loading quite slowly and the stuttering effect you are seeing is probably coming from your computer having issues with the amount of information coming in.
You can try and speed things up a bit by making less $(selector) calls.. cache some of your jQuery objects when you are using them multiple times... also remember you can chain functions on jQuery objects.. jQO.addClass("foo").removeClass("bar"); is the same as jQO.addClass("foo"); jQO.removeClass("bar");
like so...
(function(){
var signin = $("#signin"), signup = $("#signup"), signin_f = $("#signin_form"), holder = $("#holder"), signup_f = $("#signup_form"), f_container = $("#form_container");
$(".button").click(function () {
if (counter === 0) {
signin.removeClass('default_radius').addClass('right_radius');
signup.removeClass('default_radius').addClass('left_radius');
$("#first").animate({
marginTop: "-=150px",
}, 500);
}
});
signup.click(function () {
if (counter === 0) {
holder.addClass('red_border').slideDown("slow");
f_container.show();
signup_f.show(0);
} else {
signin_f.hide(0);
holder.switchClass( "green_border", "red_border", 1000 );
f_container.animate({height:"260px"},1000);
signup_f.show(0);
}
counter++;
});
signin.click(function () {
if (counter === 0) {
holder.addClass('green_border').slideDown("slow");
f_container.show();
signin_f.show(1000);
} else {
signup_f.hide(0);
holder.switchClass( "red_border", "green_border", 1000 );
f_container.animate({height:"110px"},1000);
signin_f.show(0);
}
counter++;
});
})();

Categories

Resources