Is it possible to make "for" loop in CasperJS? - javascript

It is additional question of How to stop a loop when clicking asynchronously in CasperJS
I tried this code
function execOnce(casper, i, max){
// end condition
if (i === max) {
return;
}
casper.wait(3000, function() {
var button = x('//*[#id="content"]/div[3]/a['+i+']');
if (!this.exists(button)) {
this.echo(i + " not available");
return;
}
this.thenClick(button, function (){
console.log('Searching dic');
words = words.concat(this.evaluate(getWords));
// recursive step
execOnce(this, i+1, max);
});
});
};
// start the recursive chain
casper.then(function(){
execOnce(this, 1, 200);
});
But I found that indexes' Xpath of my target web pages has iteration.
When it reached '//*[#id="mArticle"]/div[2]/a['11']' next index's Xpath becomes '//*[#id="mArticle"]/div[2]/a['2'] (back to a['2'])
for example the webpage url is "http://krdic.naver.com/search.nhn?query=%E3%85%8F%E3%85%8F&kind=keyword"
under the page there are [1][2][3][4][5][6][7][8][9][10] [Next Page]
When I click Next page you can see
[Previous Page][11][12][13][14][15][16][17][18][19][20] [Next Page]
but [12] 's Xpath is not //*[#id="content"]/div[3]/a[12] ---> It is
//*[#id="content"]/div[3]/a[2]
So I have to do iteration of function execOnce including code casper.wait(6000, function() {}
because my target website is really sensitive to query so I put "wait" code whenever I can..!
In case of this can I use nested function like this?
function execOnce(casper, i, max){
if (i === max) {
function execOnce(casper, i, max){
return;
}
...

XPath is very expressive. You can for example select the intended page link based on link text instead of link position (//div[#class='paginate']/a[text()='5']), but that alone doesn't help you much in this case.
The problem is of course that the site has a secondary pagination. You need to go to the next pagination page, before you can click of the next pagination links.
casper.wait(3000, function() {
var nextButton = x('//*[#id="content"]/div[3]/a[text()="'+i+'"]');
var lastPageNextButton = '.paginate > strong + a.next';
var button = nextButton;
if (this.exists(lastPageNextButton)) {
button = lastPageNextButton;
} else if (!this.exists(button)) {
this.echo(i + " not available");
return;
}
this.thenClick(button, function (){
console.log('Searching dic');
words = words.concat(this.evaluate(getWords));
// recursive step
execOnce(this, i+1, max);
});
});

Related

How can I toggle between 2 classes on click on the same element on JS

Need your help on something,
Trying to toggle between classes when I click on an element on an HTML page but unfortunately once toggled I cannot change it back.
Even tried returning the querySelector but no luck.
Here is the code I wrote;
let fetchNumbers = document.querySelectorAll(`.cardnumber`);
for (let index = 0; index < fetchNumbers.length; index++) {
if (fetchNumbers[index].classList.contains('unselected') === true) {
fetchNumbers[index].addEventListener(`click`, function() {
fetchNumbers[index].classList.add(`selected`);
fetchNumbers[index].classList.remove(`unselected`);
fetchNumbers = document.querySelectorAll(`.cardnumber`);
return fetchNumbers;
});
} else {
fetchNumbers[index].addEventListener(`click`, function() {
fetchNumbers[index].classList.add(`unselected`);
fetchNumbers[index].classList.remove(`selected`);
fetchNumbers = document.querySelectorAll(`.cardnumber`)
return fetchNumbers;
});
}
}
Thank you in advance
Have You tried using the toggle() method? I think that it might be what You're looking for
Give it a shot!
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_toggle_class
Another example that might be helpful
https://codepen.io/StrengthandFreedom/pen/ZOGVLg
I've created an Asp.NET Core application to test/debug your code with a fresh html page and javascript with your code snippet.
If this code block below is the only code in your Javascript file,
let fetchNumbers = document.querySelectorAll(`.cardnumber`);
for (let index = 0; index < fetchNumbers.length; index++) {
if (fetchNumbers[index].classList.contains('unselected') === true) {
fetchNumbers[index].addEventListener(`click`, function () {
fetchNumbers[index].classList.add(`selected`);
fetchNumbers[index].classList.remove(`unselected`);
fetchNumbers = document.querySelectorAll(`.cardnumber`);
return fetchNumbers;
});
} else {
fetchNumbers[index].addEventListener(`click`, function () {
fetchNumbers[index].classList.add(`unselected`);
fetchNumbers[index].classList.remove(`selected`);
fetchNumbers = document.querySelectorAll(`.cardnumber`)
return fetchNumbers;
});
}
Info
Javascript files are loaded statically, which means they are ran immediately after the html page is loaded into the DOM, so this code is run immediately after the html page is loaded.
Problem
Based upon the classes defined by your html elements (in this case - class="cardnumber unselected"), once the page loads, your code block above would hit the first if statement, then assign the click event to all cardnumber elements, then proceed to change the class from unselected to selected.
When you go to click your html element the second time, the click event from the if block in your if/else block will NOT be ran as the page has already loaded. This means the else block will never execute.
This is the reason you are not seeing any functionality on the second click.
note
Javascript events can only be defined once, so you'd never be able to bind both the click events in the if block AND else block.
Solution
Define the click event for your cardnumber. Move your if/else logic into your new click event like below:
let fetchNumbers = document.querySelectorAll('.cardNumber');
for (let index = 0; index < fetchNumbers.length; index++) {
fetchNumbers[index].addEventListener('click', function () {
if (fetchNumbers[index].classList.contains('unselected') === true) {
fetchNumbers[index].classList.add('selected');
fetchNumbers[index].classList.remove('unselected');
fetchNumbers = document.querySelectorAll('.cardNumber');
return fetchNumbers;
} else {
fetchNumbers[index].classList.add('unselected');
fetchNumbers[index].classList.remove('selected');
fetchNumbers = document.querySelectorAll('.cardNumber')
return fetchNumbers;
}
});
}

Cordova navigator.app.backHistory button on html different approach

I'm building hybrid app with Intel XDK and I need help with back button and it's function. I have only one index.html file. All "pages" are 's and each one have different id.
I navigate through them using activate_subpage("#uib_page_10");
$(document).on("click", ".firs_div_button", function(evt){
//#uib_page_10 is div with it's content
activate_subpage("#uib_page_10");
var thisPage = 1;
goBackFunction (thisPage); //call function and pass it page number
});
$(document).on("click", ".second_div_button", function(evt){
//#uib_page_20 is div with it's content
activate_subpage("#uib_page_20");
var thisPage = 2;
goBackFunction (thisPage); //call function and pass it page number
});
I have set this EventListener hardware on back button.
document.addEventListener("backbutton", onBackKeyDown, false);
function onBackKeyDown() {
alert("hello");
navigator.app.backHistory();
}
This is functional but it does not work as it should, in my case and for my app.
When I navigate from one page to another (5 pages / divs) and hit back button, sometimes it does not go back to the first page. It just go "back" to history too deep and close the app, without changing the actual page (view) before closing.
Now, I have an idea, but I need help with this.
I will not use history back, I will use counter and dynamic array for up to 5 elements.
function goBackFunction (getActivePage) {
var active_page = getActivePage;
var counter = 0; // init the counter (max is 5)
var history_list = [counter][active_page]; // empty array
counter = counter + 1;
:
:
:
}
document.addEventListener("backbutton", onBackKeyDown, false);
function onBackKeyDown() {
//read the array and it's positions then activate:
activate_subpage("#PAGE_FROM_ARRAY");
counter = counter - 1;
if (counter == 0) {
//trigger the app exit when counter get's to 0.
navigator.app.exitApp();
}
}
This is only idea, not tested. I would like to store list of opened pages in Array and when back button is pressed, to activate the pages taken from the Array list, backwards.
I do not know how to do this, I'm not a expert :( There is may be batter way to do this. If someone have any suggestion, I will accept it :D
I save an array in localStorage with all pages navigated and I go back using a pop() on the array. At the moment, it's the best way I got to go back.
This is my code:
// First, create the table "pages"
function init_pages_table()
{
var pages = new localStorageDB("pages", localStorage);
if (!pages.isNew())
{
pages.drop();
pages.commit();
}
var pages = new localStorageDB("pages", localStorage);
pages.createTable("Pages", ["nome"]);
// commit the database to localStorage
// all create/drop/insert/update/delete operations should be committed
pages.commit();
}
// Add a page into the array:
function push_pagename(pagename)
{
var pages = new localStorageDB("pages", localStorage);
if (!pages.tableExists("Pages"))
{
init_pages_table();
pages = new localStorageDB("pages", localStorage);
}
pages.insert("Pages", {nome: pagename});
pages.commit();
}
// Pop a page form the array:
function pop_pagename()
{
var output = '';
var id_page = ''
var pages = new localStorageDB("pages", localStorage);
var last_page = pages.queryAll("Pages", { limit: 1,
sort: [["ID", "DESC"]]
});
$.each(last_page, function(index,value){
output = value.nome;
id_page = value.ID;
return false;
});
var rowdeleted = pages.deleteRows("Pages", {ID: id_page});
pages.commit();
return output;
}
You can also define functions for set, get, read:
function set_backpage(pageurl)
{
push_pagename(pageurl);
}
function get_backpage()
{
return pop_pagename();
}
function read_backpage()
{
var output = '';
var id_page = ''
var pages = new localStorageDB("pages", localStorage);
var last_page = pages.queryAll("Pages", { limit: 1,
sort: [["ID", "DESC"]]
});
$.each(last_page, function(index,value){
output = value.nome;
id_page = value.ID;
return false;
});
return output;
}

Multiple $("selectort").click (function () in if then construction not working

I have a server that dynamically(asp.net ) generate webpages that I can't alter.
On all pages I would like to capture all buttons clicked.
In JSFiddle https://jsfiddle.net/forssux/aub2t6gn/2/ is an example..
$(".checkout-basket").click (function ()
The first alert shows the 3 possible values,
but not the chosen item..
$(".button.button-dl").click(function ()
In jsfiddle this part doesn't get executed
Strangely on my real webpage I get the button clicked...but when I put it in the If then construction it fails to console.log the chosen item..
I hope somebody can explain me how to get these..
Kind Regards
Guy Forssman
//$("div.detail-info,table.checkout-basket").click(function () {
// var knopje = $(this).attr("class")//.split(" ");
// console.log(knopje + " knopje was clicked");
// if(knopje.indexOf("detail-info") > -1) {
// console.log("div class detail-info is clicked");
// }
// else if (knopje.indexOf("checkout-basket") > -1) {
// console.log("table class checkout-basket is clicked");
// }
// else {
// alert ("er is op iets anderes gedrukt");
// }
// capture click on download button in checkout-basket page
$(".checkout-basket").click (function () {
basket =[];
item="";
str = $(this).text();
str = str.replace(/\s\s+/g, ' ');
var str = str.match(/("[^"]+"|[^"\s]+)/g);
console.log("Array ",str);
for(var i=0;i<str.length;i++){
if(str[i] === "verwijder"){
console.log("Item= ",str[i+1]);
item = str[i+1];
basket.push(item);}
}
console.log("Basket contains ",basket);
//console.log("idValBasket ",idVal);
var test = idVal.replace(/\$/gi, "_").slice(0,-6);
console.log("test ",test);
var element = test.substr(test.length - 2)-1;
console.log("element ",element);
element=element-1;
item = basket[element];
console.log("Item finaal is ",item);
});
$(".button.button-dl").click(function () {
var addressValue = $(this).attr('href');
console.log("addresValue Basket",addressValue );
var re = /'(.*?)'/;
var m = addressValue.match(re);
console.log (" m basket is ",m);
if (m != null)
idVal = (m[0].replace(re, '$1'));
console.log("idVal Basket",idVal);
});
//This section captures the download in the detail page
$(".button").click(function () {
var downloadItem = document.getElementsByTagName("h1")[0].innerHTML
console.log("addresValue detail",downloadItem );
});
I never use click function, use on(*event*,...) instead:
$(".checkout-basket").on("click", function (){ /* CODE */ });
Check if visually there are a layout over the a layuot (a div, span, etc.)
Maybe a strange question and maybe i got it wrong, but why do you use push ?? if you want to delete an item ? btw also the example isn't working so maybe that is your problem

Two divs click event works only on one div

I'm using jquery and ajax to create a drawer (#DrawerContainer) and load content into it if I click a thumbnail in a gallery. My function is almost finished but I want to be able to close that drawer if I click again the opening button (now #current).
Here is a jsfiddle of my code: http://jsfiddle.net/RF6df/54/
The drawer element appears if you click a square/thumbnail, it's the blueish rectangle.
The current thumbnail is turned green.
I added a button in my drawer (not visible in the jsfiddle) to close it. I use this part of code for this purpose and it's working like a charm.
// Close the drawer
$(".CloseDrawer").click(function() {
$('#DrawerContainer').slideUp()
setTimeout(function(){ // then remove it...
$('#DrawerContainer').remove();
}, 300); // after 500ms.
return false;
});
Now I need my #current div to be able to close #DrawerContainer the same way .CloseDrawer does in the code above. Unfortunately adding a second trigger like this $("#current,.CloseDrawer").click(function() to my function isn't working... When clicking my "current" thumbnail, it just reopen the drawer instead of closing it...
How can I modify my code to close my #DrawerContainer with the "current" thumbnail?
Please keep in mind that I'm learning jquery, so if you can comment it could be of a great help. And please do not modify my markup or css, since everything works beside the closing part.
As per my understanding, you can use "toggle()" function which does exactly the same (i.e, toggle visiblity).
$('#DrawerContainer').toggle();
EDIT:
Updated the script to work.
$(document).ready(function() {
$.ajaxSetup({cache: false});
$('#portfolio-list>div:not(#DrawerContainer)').click(function() {
if ($(this).attr("id") != "current")
{
// modify hash for sharing purpose (remove the first part of the href)
var pathname = $(this).find('a')[0].href.split('/'),
l = pathname.length;
pathname = pathname[l-1] || pathname[l-2];
window.location.hash = "#!" + pathname;
$('#current').removeAttr('id');
$(this).attr('id', 'current');
// find first item in next row
var LastInRow = -1;
var top = $(this).offset().top;
if ($(this).next().length == 0 || $(this).next().offset().top != top) {
LastInRow = $(this);
}
else {
$(this).nextAll().each(function() {
if ($(this).offset().top != top) {
return false; // == break from .each()
}
LastInRow = $(this);
});
}
if (LastInRow === -1) {
LastInRow = $(this).parent().children().last();
}
// Ajout du drawer
var post_link = $(this).find('.mosaic-backdrop').attr("href");
$('#DrawerContainer').remove(); // remove existing, if any
$('<div/>').attr('id', 'DrawerContainer').css({display: 'none'}).data('citem', this).html("loading...").load(post_link + " #container > * ").insertAfter(LastInRow).slideDown(300);
return false; // stops the browser when content is loaded
}
else {
$('#DrawerContainer').slideUp(300);
$(this).removeAttr("id");
}
});
$(document).ajaxSuccess(function() {
Cufon('h1'); //refresh cufon
// Toggle/close the drawer
$("#current,.CloseDrawer").click(function() {
$('#DrawerContainer').slideToggle()
setTimeout(function(){ // then remove it...
$('#DrawerContainer').remove();
}, 300); // after 500ms.
return false;
});
});
//updated Ene's version
var hash = window.location.hash;
if ( hash.length > 0 ) {
hash = hash.replace('#!' , '' , hash );
$('a[href$="'+hash+'/"]').trigger('click');
}
});
Also, updated it here: Updated JS Fiddle
EDIT -2: Updated Link
Hope this Helps!!

Click an anchor in a UIWebView with a UIButton

I have a Javascript function on my webpage that I'm displaying in a UIWebView:
$(document).ready(function() {
// index to reference the next/prev display
var i = 0;
// get the total number of display sections
// where the ID starts with "display_"
var len = $('div[id^="hl"]').length;
// for next, increment the index, or reset to 0, and concatenate
// it to "display_" and set it as the hash
$('#next').click(function() {
++i;
window.location.hash = "hl" + i;
return false;
});
// for prev, if the index is 0, set it to the total length, then decrement
// it, concatenate it to "display_" and set it as the hash
$('#prev').click(function() {
if (i > 1)
--i;
window.location.hash = "hl" + i;
return false;
});
});
So what I need to do is simulate an anchor click when my UIButton is clicked:
- (IBAction)next:(id)sender {
[animalDesciption stringByEvaluatingJavaScriptFromString:#"document.getElementById(\"next\").click();"];
}
But this doesn't work!
It works great on an HTML page, just by clicking the anchor that has an id of "next".
Any ideas why this won't work when clicking the button?
BTW I am able to call a standard javascript function like myFunc() with my current setup, but it won't do anything like this!
Any thoughts would be very appreciated!
You could implement javascript functions for next and previous and call that directly from your UIButton.
var i = 0;
function next() {
++i;
window.location.hash = "hl" + i;
return false;
}
function prev() {
if (i > 1)
--i;
window.location.hash = "hl" + i;
return false;
}
$(document).ready(function() {
// get the total number of display sections
// where the ID starts with "display_"
var len = $('div[id^="hl"]').length;
$('#next').click(function() {
next();
});
$('#prev').click(function() {
prev():
});
});
The call from your UIButton would be:
- (IBAction)next:(id)sender {
[animalDesciption stringByEvaluatingJavaScriptFromString:#"next()"];
}
By the way: I think you forgot to use len in the next() function to avoid stepping over the last display section.

Categories

Resources