How to click on selectbox options using PhantomJS - javascript

There is the page testkrok.org.ua with a consistent selection of parameters. So, I need to create a series of 5 clicks on each of the options of 5 select boxes that depend on each other.
document.querySelector('select.se1')[3]
document.querySelector('select.se2')[1]
document.querySelector('select.se3')[1]
document.querySelector('select.se4')[1]
document.querySelector('select.se5')[3]
to redirect to the page with tests.
But on snapshot taken after the first click the second panel does not appear?
Maybe I don't hit the the element?
var page = require('webpage').create();
page.open('https://testkrok.org.ua', function(status) {
console.log("Status: " + status);
if(status === "success") {
page.evaluate(function() {
var theEvent = document.createEvent("MouseEvent");
theEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
var element = document.querySelector('select.se1')[3];
element.dispatchEvent(theEvent);
});
}
setTimeout( function() {
page.render('snapshot.png');
phantom.exit()
}, 5000);
});

You can't click (trigger a click event) on options of a select box. You need to change the selected option and then trigger a change event. For example:
var sel = document.querySelector('select.se1');
sel.selectedIndex = 2;
var event = new UIEvent("change", {
"view": window,
"bubbles": true,
"cancelable": true
});
sel.dispatchEvent(event);
You can package that in a function
function selectOption(selector, optionIndex) {
page.evaluate(function(selector, optionIndex){
var sel = document.querySelector(selector);
sel.selectedIndex = optionIndex;
var event = new UIEvent("change", {
"view": window,
"bubbles": true,
"cancelable": true
});
sel.dispatchEvent(event);
}, selector, optionIndex);
}
Then you can call it one after the other
selectOption("select.se1", 2);
selectOption("select.se2", 0);
selectOption("select.se3", 0);
...
You get the idea. In case the onChange event of the select box needs remote data for example through AJAX, then you will need to wait between the calls. Either use a static wait time (see following example) or use waitFor().
setTimeout(function(){
selectOption("select.se1", 2);
}, 1000);
setTimeout(function(){
selectOption("select.se2", 0);
}, 2000);
setTimeout(function(){
selectOption("select.se3", 0);
}, 3000);
...

Related

Clicking on button in whatsapp web using javascript

I've the following code. What I'm trying to do is hover over the msg-container (message, on whatsapp web), click on the triangle context button that appears, and click on "Download"
I'm trying to automate downloading voice messages (the only messages that have download button on them are voice messages or media I think, and also I'm executing this code when "starred messages" tab is open)
But for some reason, it only hovers over the message, the triangle appears, it clicks on it but then it doesn't click on the download button.
I know this is really specific but to reproduce this you can star 2 or 3 voice messages on whatsapp web, go to starred messages tab on the browser, open the console and paste the code.
var messageContainers = document.querySelectorAll('[data-testid="msg-container"]');
for (var i = 0; i < messageContainers.length; i++) {
setTimeout(function(container) {
// Simulate a mouseover event on the container element
var mouseoverEvent = new MouseEvent('mouseover', {
view: window,
bubbles: true,
cancelable: true
});
container.dispatchEvent(mouseoverEvent);
// Wait for the "down context" button to appear, then simulate a click event on it
setTimeout(function() {
var tri = container.querySelector('[data-testid="icon-down-context"]');
if (tri) {
var clickEvent = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
tri.dispatchEvent(clickEvent);
// Wait for the download button to appear, then simulate a click event on it
setTimeout(function() {
var downloadButton = container.querySelector('[data-testid="mi-msg-download"]');
if (downloadButton) {
downloadButton.dispatchEvent(clickEvent);
}
}, 1000);
}
}, 1000);
}, 1000 * i, messageContainers[i]);
}
For some reason this one worked. Just in case if anyone else needs it.
var messageContainers = document.querySelectorAll('[data-testid="msg-container"]');
for (var i = 0; i < messageContainers.length; i++) {
setTimeout(function(container) {
// Simulate a mouseover event on the container element
var mouseoverEvent = new MouseEvent('mouseover', {
view: window,
bubbles: true,
cancelable: true
});
container.dispatchEvent(mouseoverEvent);
setTimeout(function() {
// Find the triangle element and simulate a click event on it
var tri = container.querySelector('[data-testid="down-context"]');
if (tri) {
var clickEvent = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
tri.dispatchEvent(clickEvent);
// Wait for the context menu to appear
setTimeout(function() {
// Find the download button and simulate a click event on it
var downloadButton = document.querySelector('li[data-testid="mi-msg-download"] > div._1MZM5');
if (downloadButton) {
downloadButton.dispatchEvent(new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
}));
}
}, 500);
}
},500);
}, 1000 * i, messageContainers[i]);
}

How to trigger an element by id? (".click();" doesn't work)

I want to trigger this element in Google Translate, so that it would always auto-correct everything I type.
https://i.snag.gy/NRsWFB.jpg
The element's id is "spelling-correction".
I tried this:
document.getElementById('spelling-correction').click();
And this:
function clickLink(link) {
var cancelled = false;
if (document.createEvent) {
var event = document.createEvent("MouseEvents");
event.initMouseEvent("click", true, true, window,
0, 0, 0, 0, 0,
false, false, false, false,
0, null);
cancelled = !link.dispatchEvent(event);
}
else if (link.fireEvent) {
cancelled = !link.fireEvent("onclick");
}
if (!cancelled) {
window.location = link.href;
}
}
setInterval(function copyText() {
var correction123 = document.getElementById("spelling-correction");
correction123.clickLink();
}, 100);
But they don't work unfortunately. I would like to somehow trigger this "spelling-correction", so that whatever I write would be auto-corrected.
Thank you in advance!
The issue is you're clicking on a div. Divs do nothing when clicked (unless otherwise specified).
Since what you want seems to be clicking on the link itself, you should try something like this instead:
childAnchors = document.querySelectorAll("#spelling-correction > a");
childAnchors[0].click();

smoothstate.js - back button sometimes doesn't work?

I'm using the plugin smoothstate.js on my website. For some reason, every now and again when I navigate through the pages using the back and forward buttons, the back button stops working.
The URL changes but the content remains the same?
I've checked the console for errors this is displaying:
TypeError: Cannot read property 'state' of undefined
Does anyone know why this is happening? Like I said, the majority of the time it works okay but all of sudden it doesn't.
The code I'm using is like so:
$(function(){
'use strict';
var options = {
prefetch: true,
debug:true,
cacheLength: 0,
repeatDelay: 500,
onStart: {
duration: 0, // Duration of our animation
render: function ($container) {
// Add your CSS animation reversing class
$container.addClass('is-exiting');
// Restart your animation
smoothState.restartCSSAnimations();
}
},
onProgress: {
// How long this animation takes
duration: 0,
// A function that dictates the animations that take place
render: function ($container) {
$container.addClass('is-loading');
$('#progressBar').append('<div id="bar"></div>');
var progress = '100%';
$('#bar').animate({
width: progress
}, 400);
}
},
onReady: {
duration: 0,
render: function ($container, $newContent) {
$container.removeClass('is-loading is-exiting');
// Inject the new content
$container.html($newContent);
},
},
onAfter: function() {
navbarAnimate();
closeMenu();
ImageSliders();
initPhotoSwipeFromDOM('.gallery');
ImageOverlay();
window.parsePinBtns();
backToTop();
}
},
smoothState = $('#main').smoothState(options).data('smoothState');
});
I also ran into this issue which happens when the back and forward buttons are clicked too fast without the page fully loading. A hacky solution for me was to reload the page if page.cache[page.href] is undefined.
/** Handles the popstate event, like when the user hits 'back' */
onPopState = function(e) {
if(e.state !== null || typeof e.state !== undefined) {
var url = window.location.href;
var $page = $('#' + e.state.id);
var page = $page.data('smoothState');
if (typeof(page.cache[page.href]) !== 'undefined') {
var diffUrl = (page.href !== url && !utility.isHash(url, page.href));
var diffState = (e.state !== page.cache[page.href].state);
if(diffUrl || diffState) {
if (diffState) {
page.clear(page.href);
}
page.load(url, false);
}
}
else {
//reload the page if page.cache[page.href] is undefined
location.reload();
}
}
},

How to go to the next page for scraping in PhantomJS

I'm trying to get several elements from a website with several pages. I'm currently using PhantomJS to do that work and my code almost works, but the issue is that my code scrapes twice the first page even if (according to the log) it seems that I already moved to the second one.
Here's the code:
var page = require('webpage').create();
page.viewportSize = { width: 1061, height: 1000 }; //To specify the window size
page.open("website", function () {
function fetch_names(){
var name = page.evaluate(function () {
return [].map.call(document.querySelectorAll('div.pepitesteasermain h2 a'), function(name){
return name.getAttribute('href');
});
});
console.log(name.join('\n'));
page.render('1.png');
window.setTimeout(function (){
goto_next_page();
}, 5000);
}
function goto_next_page(){
page.evaluate(function () {
var a = document.querySelector('#block-system-main .next a');
var e = document.createEvent('MouseEvents');
e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(e);
waitforload = true;
});
fetch_names();
}
fetch_names();
});
You can try it by yourself to understand how all of that work.
You need to wait for the page to load after you click and not before you click by moving setTimeout() from fetch_names to goto_next_page:
function fetch_names(){
var name = page.evaluate(function () {
return [].map.call(document.querySelectorAll('div.pepitesteasermain h2 a'), function(name){
return name.getAttribute('href');
});
});
console.log(name.join('\n'));
page.render('1.png');
goto_next_page();
}
function goto_next_page(){
page.evaluate(function () {
var a = document.querySelector('#block-system-main .next a');
var e = document.createEvent('MouseEvents');
e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(e);
waitforload = true;
});
window.setTimeout(function (){
fetch_names();
}, 5000);
}
Note that there are many more ways to wait for something other than the static timeout. Instead, you can
register to the page.onLoadFinished event:
page.onLoadFinished = fetch_names;
wait for a specific selector to appear with the waitFor() function from the examples.

Hide all instances of element except one currently being written / displayed

I have this notification system that works with the following jQuery / javascript and displays a notification when called.
What I am having some trouble doing and what I am trying to do is once a new notification is create to hide and remove / destroy any existing notifications.
I've tried something like this: $('.notification').not(this).hide().remove();, but that didn't work.
Here is the jQuery behind the notifications:
;(function($) {
$.notificationOptions = {
className: '',
click: function() {},
content: '',
duration: 5000,
fadeIn: 400,
fadeOut: 600,
limit: false,
queue: false,
slideUp: 200,
horizontal: 'right',
vertical: 'top',
afterShow: function(){},
afterClose: function(){}
};
var Notification = function(board, options) {
var that = this;
// build notification template
var htmlElement = $([
'<div class="notification ' + options.className + '" style="display:none">',
'<div class="close"></div>',
options.content,
'</div>'
].join(''));
// getter for template
this.getHtmlElement = function() {
return htmlElement;
};
// custom hide
this.hide = function() {
htmlElement.addClass('hiding');
htmlElement.animate({ opacity: .01 }, options.fadeOut, function() {
var queued = queue.shift();
if (queued) {
$.createNotification(queued);
}
});
htmlElement.slideUp(options.slideUp, function() {
$(this).remove();
options.afterClose();
});
};
// show in board
this.show = function() {
// append to board and show
htmlElement[options.vertical == 'top' ? 'appendTo' : 'prependTo'](board);
htmlElement.fadeIn(options.fadeIn, options.afterShow());
//$('.notification').css('marginLeft', -$('.notification').outerWidth()/2);
$('.notification-board.center').css('marginLeft', -($('.notification-board.center').width()/2));
$(window).on('resize', function(){
$('.notification-board.center').css('marginLeft', -($('.notification-board.center').width()/2));
});
};
// set custom click callback
htmlElement.on('click', function() {
options.click.apply(that);
});
// helper classes to avoid hide when hover
htmlElement.on('mouseenter', function() {
htmlElement.addClass('hover');
if (htmlElement.hasClass('hiding')) {
// recover
htmlElement.stop(true);
// reset slideUp, could not find a better way to achieve this
htmlElement.attr('style', 'opacity: ' + htmlElement.css('opacity'));
htmlElement.animate({ opacity: 1 }, options.fadeIn);
htmlElement.removeClass('hiding');
htmlElement.addClass('pending');
}
});
htmlElement.on('mouseleave', function() {
if (htmlElement.hasClass('pending')) {
// hide was pending
that.hide();
}
htmlElement.removeClass('hover');
});
// close button bind
htmlElement.children('.close').on('click', function() {
that.hide();
});
if (options.duration) {
// hide timer
setTimeout(function() {
if (htmlElement.hasClass('hover')) {
// hovering, do not hide now
htmlElement.addClass('pending');
} else {
that.hide();
}
}, options.duration);
}
return this;
};
var queue = [];
$.createNotification = function(options) {
options = $.extend({}, $.notificationOptions, options || {});
// get notification container (aka board)
var board = $('.notification-board.' + options.horizontal + '.' + options.vertical);
if (!board.length) {
board = $('<div class="notification-board ' + options.horizontal + ' ' + options.vertical + '" />');
board.appendTo('body');
}
if (options.limit && board.children('.notification:not(.hiding)').length >= options.limit) {
// limit reached
if (options.queue) {
queue.push(options);
}
return;
}
// create new notification and show
var notification = new Notification(board, options)
notification.show(board);
return notification;
};
})(jQuery);
and here is how the notifications are called / created:
$.createNotification({
horizontal:'center',
vertical:'top',
content:'No more cards at this time.',
duration:6000,
click:function(){
this.hide();
}
});
The code:
$('.notification').not(this).hide().remove();
will work just fine to remove all .notification DOM elements currently in the DOM except the current one IF this is the current notification DOM element. If that code isn't working, then it's likely because this isn't the desired notification DOM element that you want to keep. If this is an instance of your Notification class, then that's the wrong type of object. For that above code to work, this has to be the notification DOM object.
If you want to just remove all old notification DOM elements BEFORE you insert your new one, then you can just do this before your new one is in the DOM:
$('.notification').remove();
That will clear out the old ones before you insert your new one.
Since you don't have this line of code in your currently posted code, I can't tell where you were trying to use it so can't advise further on what might be wrong. Please describe further where in your code you were trying to use this.

Categories