I am using the Infinite Scroll plugin, for Wordpress, to load new posts on to the page when users scroll to the bottom. The problem is this has been loading duplicate posts, as the sorting changes extremely fast (due to popularity) and posts end up on different pages.
When the plugin grabs the next page, sometimes products that were originally on the FIRST page have been sorted to the SECOND page. So I end up with duplicates.
I was planning on waiting for the script to load the next page content, then loop through all of the post titles and locate the duplicates. Then I would remove the second instance of each post.
I noticed Infinite Scroll has a window in the settings labeled "Run after content is loaded/callback" so I thought I could enter a function in that field to be called.
removeDuplicates();
Then I entered something like this in the footer:
function removeDuplicates(){
var titleList = [];
$('.title').each(function(i, obj) {
/* The titles are in <h1> tags, I cycle through them,
if it's the first time seeing the title I add it to titleList.
If it's already in the array I hide the parent. */
});
});
I keep getting "undefined function" related to the .each, and it seems like it has something to do with scope but I'm not sure what's happening.
Is there an easier way to trigger the function to remove the duplicates? Am I at least on the right track?
Thanks for any insight you can provide!
It seems like jquery isn't loaded. Even if you use a selector that returns no elements, it should still have the .each() method.
Try using vanillaJS instead of jquery.
var titleNodeList = document.querySelectorAll('.title'); // get elements
var titleElArray = Array.prototype.slice(titleNodeList); // convert to array
titleElArray.forEach(function(el){...}); // iterate
Related
Updated
I'm using:
Selenium 2.53.1
Firefox and IE11
I've been trying to click on all elements with the same selector, for example, I want to click on all the ones with the title "What I Want":
<div id="first_question">
<a class="gibberish1" title="What I Want"></a>
<a class="gibberish2" title="What I Want"></a>
<a class="gibberish3" title="What I Want"></a>
</div>
Here is what I have working so far:
browser.findElements(by.xpath("//a[#title='What I Want']")).then(function(all_tests){
for (var i = 0; i < all_tests.length; i++) {
console.log(all_tests.length);
all_tests[i].click();
}
});
It's able to recognize that I have three elements, and if I call each individual one directly then I'm able to see it click on the button. However, when I want to loop so it clicks on each button, I get an error:
"StaleElementReferenceError: Element is no longer attached to the DOM."
I also added a wait of 5 seconds in, but that didn't deter the same issue from popping up.
What am I doing wrong? I'm new to Selenium and I'm trying to figure this out in Javascript, instead of Java which is what I find examples for this situation.
You need to find them one by one, on page reload your objects will be lost and you will get stale element exception.
1) find all links
2) save an attribute/attributes in a list/array that can help you identify each link
3) create a loop where for each attribute you are searching for the element and click it
Moving my comment to an answer...
The first issue is that your code attempts are clicking a collection rather than an individual element. There are several ways to do this, one of which is doing a for loop and accessing the individual elements inside the loop, e.g. all_tests[i].click();.
The second issue with stale elements is because the DOM is being changed either through refreshing the page or navigating to a new page. If the issue is that clicking is causing navigation, you can loop through all the A tags and store the hrefs in a string array. Then you can loop through that string array and navigate to each stored href, etc.
Another way to handle this is to scrape the page using the index rather than storing the collection outside the loop. A simple example of this is
This is Java/pseudocode that you will have to translate into whatever language you are using but the concept should work.
for (int i = 0; i < driver.findElements(locator).length; i++)
{
// scrapes the page each iteration to avoid the stale element exception
driver.findElements(locator)[i].click();
// do stuff and get back to the original page, if navigated
}
I think first print the all_tests, check the output and the changes in
the for loop could work
for element in all_tests: element.click()
And to handle StaleElementReferenceError, can check here:
StaleElementReferenceException on Python Selenium
NOTE: Not Tested but handled the same concern.
My main mission: Is to get the text of the next and the previous objects to the chosen object - To display the image (and its titles) Previous & Next.
Before that I have a problem: to get text of a selected object, from an index to a variable.
The problem: Every time I pick a random object, the variable does not change but the text is added to the existing text in the index.
I made a DEMO, would appreciate your help.
$(document).ready(function hintProject(){
$('#nextProject, #prevProject').click(function(){
subtitle = null;
subtitle = $('#client-sub.active').justtext();
$('#next_target_title').text(subtitle);
alert (' text::: ' + subtitle );
});
});
It looks like jQuery simply can't find the objects you're specifying. I don't think the problem is with the snippet in the question. I think the problem is with your document ready function.
To debug, try simplifying your problem by cutting out all of the additional complexity of the setup script and just set up an HTML page that is in the state you want. It's much easier to understand 1 problem than 2 or more.
Also, try simplifying how you're specifying an active item: a single class on the portfolio item would make your life easier. Then you can specify css and such based on the parent instead of adding multiple classes to multiple things inside the each portfolio item.
Is ther a Jquery way to combine append() and replaceWith()?
I have a login form in a JQM project, sitting on every site. As multiple pages will be loaded into the DOM, I need to move the long form "along", as the user goes from page to page, to avoid having duplicate form#ids in the DOM.
My question:
I can do like this:
$('.form_in_new_page').replaceWith('.form_in_old_page')
But is there a way to "append and replace", so I can do this with one line of code?
Thanks for help!
EDIT: some more info
Here is my script which I'm running on pagebeforehide:
$('div:jqmData(role="page").basePage').on('pagebeforehide', function(e, data) {
// look for unique elements on the page being left
$(this).find(':jqmData(unique="true")').each(function(){
var uniqueID = $(this).jqmData("unique-id"),
nextPage = data.nextPage,
nextUnique = nextPage.find( ":jqmData(unique-id='"+uniqueID+"')" ),
nextUniqueID = nextUnique.jqmData('unique-id') === uniqueID;
// if a unique element with id=123 is on both from and next page
if ( nextUniqueID == true ) {
// append element from page being left to and replace it on next page
nextUnique.replaceWith( $(this) );
}
});
});
I need to keep my pages inside the DOM in my app. All pages have a login/logout popup, which includes a form and inputs with ids. So if I have 10 pages in my DOM I will have 10 popups with 10 forms and 10x each ID. I'm inserting the popups automatically on every so they are there if the user calls a subpage directly. However as soons as the user goes to the next page I need to make sure the form on the first page appends and replaces the form on the 2nd page.
Hope it's clear now.
The above only replaces the form on the new page, but still leaves it in the old page.
In jquery since functions return this ($) you can chain calls:
$("#selector").replaceWith("<div>Hello</div>").append('<div>test2</div>');
Of corse, that means you need to chain calls in the correct order.
My page contains some small wizard which I built using jQuery.
I would like to offer the user to restart/reset the wizard and start it from the beginning level.
There is anyway to do it without refreshing the page?
One way to achieve this would be to clone() the wizard's element tree in its initial state and store it in a global variable or in the document's data:
$(document).data("initialWizard", $("#yourWizard").clone(true));
(Note that passing true to clone() also clones element data and event handlers, which might prove handy in your case.)
Then, when you want to restore the wizard to its original state, you can do:
$(document).data("initialWizard").replaceAll("#yourWizard");
The only way to start over without refreshing the page is for you to manually return the DOM to the state it was in when the page loaded and to restore any javascript state too. You would either have to record/remember the initial state so you could go back to it or keep track of all your incremental changes so you could go back there too.
If you really want to start over, what's wrong with a refresh?
Similar to the above, here's what I ended up doing. I stored the entire jQuery doc contents in a bootup() function (and ran it). The initial contents of the body was stored in an originalDOM variable. Then within I had a playAgain function that would set the contents of the body to that originalDOM, and run the entire jQuery contents again by calling the bootup() function. This would all be triggered by clicking on a button of class ".startover".
bootup();
var bootup = function(){$(document).ready(function(){
var originalDOM=document.body.innerHTML;
//jQuery lines of code here
var playAgain = function(){
$(".startover").click(function(){
document.body.innerHTML = originalDOM;
bootup();
});
};
playAgain();
});};
Hope that helps for anyone looking for something similar, and happy to hear feedback if there's anything I could've improved on!
First of all I would like to say that while this is the first time i post here these boards have helped me much.
With that said, I have got a strange issue regarding AJAX and scripts.
You see, in my web application i used custome JS context menus. Now each of them menus is implemented with specific features depending on the object and if the object exists.
E.x : if we got an upper menu place holder but no upper menu the context menu will have one option which is "add menu".
But say we already have the upper menu the context menu will have different options such as "edit menu" etc...
so far so good, however, say we have an upper menu place holder and no menu and then we added the menu (still no refresh on the page) i need to generate a new context menu and inject it right? so i do just that along with the new menu i just built.
all that code goes into the SAME div where the old context menu script and upper menu place holder were so basicaly they are overwriten.
Now the menu itself is in HTML so it overrides the current code the JS however acts wierd and will show now 2 context menus the old one and the new one even though i overwrite it's code.
I need to some how get rid of the old context menu script without refreshing the page.
Any ideas?
P.S
all the JS are dynamicaly generated if that makes any difference (i dont think it does.)
Well after some head breaking i figured it out..
(the problem not the solution yet) this is the ajax function right?
$.ajax({
type: "GET",
url: "../../../Tier1/EditZone/Generate.aspx?Item=contentholder&Script=true",
dataType: "html",
success: function (data) {
$('#CPH_Body_1_content_holder').html(data);
}
});
now they function uses a page with an event handler, that event handler reutnrs the data as followed response.write(answer) it just hit me that when you use response.write it sends the code after it's been compiled and ran in our case at page Generate.aspx.
so the script will run but not in the page i intended it to run and because of that i cannot overwrite it... how silly of me.
what i think ill do it return the data as an actualy string and then and only then inject the code into the container div.
ill let you folks know if that works out.
cheers and thanks for the advice these forums rock.
No matter what anyone says, do not use EVAL. It's evil and will give you memory issues if used more than a few times on a page.
See my soluition here: trying to call js code that is passed back from ajax call
Basically, create a div with the ID of "codeHolder" and voila. You'll basically want to pass your HTML and JS back to the AJAX receiver (separated by a separator), parse it on the JS side, display the HTML and put the JS Code in your javascriptCode variable.
//Somehow, get your HTML Code and JS Code into strings
var javascriptCode="function test(){.....}";
var htmlCode="<html>....</html>";
//HTML /////////////////////////////////////////
//Locate our HTML holder Div
var wndw=document.getElementById("display");
//Update visible HTML
wndw.innerHTML = htmlCode;
//Javascript ///////////////////////////////////
//Create a JSON Object to hold the new JS Code
var JSONCode=document.createElement("script");
JSONCode.setAttribute("type","text/javascript");
//Feed the JS Code string to the JSON Object
JSONCode.text=javascriptCode;
//Locate our code holder Div
var cell=document.getElementById("codeHolder");
//Remove all previous JS Code
if ( cell.hasChildNodes() )
while ( cell.childNodes.length >= 1 )
cell.removeChild( cell.firstChild );
//Add our new JS Code
cell.appendChild(JSONCode);
//Test Call///////////////////////////////////////
test();
This code will replace all previous JS code you might have put there with the new JS Code String.
Thanks for the replies.
Dutchie - that's exactly what I did. now the thing is the HTML is properly overwritten (I didn't use append I overwrote the entire div) and yes the javascript just keeps on caching...
I tried to disable browser cache and still the problem persists i get multiple context menu per item the more I ran the ajax function...
Jan,
My AJAX function builds a div tag and script tags and places them into another container div tag in the page.
What's suppose to happen is that every time the AJAX runs the code inside the container div is overwritten and you get an updated version.
the div inside the container div is overwritten yet the script tags somehow are cached into the memory and now each time the out jQuery function calls the context menu i get multiple menus...
I don't think code is needed but I will post it tomorrow.
Any ideas?