Chrome Extension, Append Element & Re-Append Across Page Changes - javascript

I have a Chrome Extension that, when its button is clicked, appends an element to the current page. However, I want to keep that element across pages; i.e., if the user clicks on a link, the new page loads, and then the element is appended.
I tried using window.onhashchange to detect page changes, check if the element was appended, and if not, append it, but that didn't seem to have any affect at all. Any tips on how to do this, or if it's possible? (preferably in javascript, no jquery)

some general description how would I do it, would be:
-when user clicks the button, append element and send msg to background to store that tab (ID)
-add listeners chrome.tabs.onCreated.addListener & onUpdated (in bg script)
-test newly created and updated tabs against your stored
-if newly created/updated tab is child of your stored tab (tabs), inject script for element appending or send msg to content script to append element
sample for BG script to start with:
chrome.tabs.onCreated.addListener(function(tab) {
console.log('tab id is'+tab.id);
console.log('opener is'+tab.openerTabId);
});
cople of things to be careful:
-openerTabId is undefined if link is dragged (extension for dragging links)
-those events are fired multiple times, so if you are sending msg to content script to append your element (or injecting), check it first
something like
if (yourElement) return;
else //append element
maybe there are better/simpler ways to achieve what you want... can't think of it right now
EDIT:
you actually don't need initial messaging when button is clicked, because listener for that button is already in bg script, and the whole process is very simple.

Related

Handling updated content in static HTML with Selenium

I am using Selenium to automate our document downloading tasks. The function I have looks like this:
def download_csv()
i = 2
while True:
try:
button_main_page=wait.until(ec.element_to_be_clickable((By.XPATH,'/.../div[{}]/button'.format(i)))
button_main_page.click()
i = i+1
button2=wait.until(ec.element_to_be_clickable((By.XPATH,'/...')))
button2.click()
download_button = wait.until(ec.element_to_be_clickable((By.XPATH,'/...')))
download_button.click()
driver.back()
driver.back()
except:
Next_Page = wait.until(ec.element_to_be_clickable((By.XPATH,'/...')))
Next_Page.click()
download_csv()
The main page has a table containing 10 elements and a button for each. To download each document, I need to navigate to the download location by doing 3 clicks and return to the main page to repeat the operation for the next element in the table, after that I am looking to repeat it on the next pages.
My issue is after I download the first element on the second page and do
driver.back()
I get returned to the original first page and start accessing the same elements again. The page url doesn't change and there is nothing I see going off in the Network tab when I click on "Next Page" button. I believe the content update is triggered when the next page button is clicked, however I have very limited knowledge of AJAX/js etc to know where to start looking.
Is it possible to bypass something like this with Selenium? And how do I look for what is triggering the table content update, if nothing is displayed in the network tab during button clicking.
Thanks!

How to find the first element if Xpath returns a list of elements(selenium-python)

browser = webdriver.Chrome(path)
browser.maximize_window()
browser.get("https://www.flipkart.com")
browser.find_element_by_xpath('//button[#class="_2AkmmA _29YdH8"]').click()
browser.find_element_by_name("q").send_keys("Mobiles")
browser.find_element_by_xpath("//button[#class='vh79eN']").click()
p = browser.find_elements_by_xpath("//div[#class='_3wU53n']")
Using the above code, I have to search for Mobiles in flipkart and click on any Mobile which should that open page onto new Tab.
I am facing issue with locating a mobile as Xpath returns a list of Mobiles. I want to find the first element and click so that it opens in new tab.
Can Anyone help me with this? Thanks!
When you used browser.find_elements_by_xpath("//div[#class='_3wU53n']") p will be a lists, so you can access items as easy as p[0], more info here.
But if you only need the first element, I suggest u should use browser.find_element_by_xpath("//div[#class='_3wU53n']") without the "s" as it should select the first element in that class
You can store in List by using list class in selenium.
List lists=driver.findElements(By.xpath(".."));
lists.get(0).click();
//Above code for click first element in lists
Once you "CLICK" the SEARCH button, the webpage redirects to a new page which shows the list of mobile. However, it takes some time to load the page. So you need to add some delay to it, so as to perform the next actions.
<the code above remains the same>
browser.find_element_by_xpath("//button[#class='vh79eN']").click()
import time
time.sleep(3) # Added a delay
browser.find_element_by_xpath("//div[#class='_3wU53n']").click() # Changed the "elements" to "element"
This opens the first mobile phone in the new tab.
PS. This code works great if you need only the first mobile. If you need all the mobiles on the page, you will have to create a loop for it!

How to prevent click events from overlapping each other?

Please see image below for error:
As shown, there is conflicting text. The initial state of the website has 6 blocks with different colors with text in the center. When clicked on, more information is displayed in the middle section. However, when you click on another box the background color is changed and/or the text inside each box is either shown or hidden.
I suspect this is happening due to my incorrect use of jQuery
I tried to fixed it hands on but still doesn't work that well because you have to click on the field with which you opened the field to close it again. you should build a click handler for the closing icon, then it should work.
https://codepen.io/felixhaeberle/pen/qGjpVJ
First, I check each Item if it is blocked and if it's blocked, return.
Then I set all items to blocked and after unblock the current (this) item.
A closing click handler (close icon) should remove all blocked entry's like $('.closing-link').click(function(){ allItems.toggleClass("blocked"); // your closing code follows here });
$("#fbPageDesign").on('click', function(){
if ($(this).hasClass("blocked")) return;
allItems.toggleClass('blocked');
$(this).toggleClass("blocked");
$(".box").toggleClass("bg-color2");
$(".boxText,.whiteRectangle").toggle();
});
a snippet out of my solution.

Do something with the particular element even though click event is fired on class name

I have a scenario where a dropdown launcher menu should appear on every row in a list page. I have tweaked the code here.
I want the popover (open) behavior be restricted for the click on the particular launcher-icon, though close action is perfect here.
the problem is when any icon is clicked, it shows all menus. My page has rows inflated from a database and every row has three such launcher icons.
I guess this block of code needs some tweaks:
// Click event handler to toggle dropdown
$(".button").click(function(event){
event.stopPropagation();
// How can I call toggle for the specific div element?
$(".app-launcher").toggle();
});
How can it be done?
You need to traverse the DOM to find the .app-launcher instance which is related to the clicked .button element, to do that use closest() and find(), like this:
$(".button").click(function(event){
event.stopPropagation();
$(this).closest('.launcher').find(".app-launcher").toggle();
});
Updated CodePen
I would also suggest looking in to using a single instance of .app-launcher and moving it around the DOM as required to DRY up your HTML code.

Same ID's in jQuery Tabs

I am using the jQuery Tabs library in a small application. The page has 5 tabs and the content for each page is loaded using Ajax. The problem is, once I load a tab, it remains in the browsers memory (and so do its HTML elements). So if I use lets say a DIV element with the same ID as a previously loaded tab, all JS function related to that ID try to interact with the old tab.
IN other words, lets say I have a page with 2 tabs, "Traffic Data1", "Traffic Data2". Now first, I click on the Traffic Data1 tab which makes the ajax call and loads the page just fine. This page, has 2 date input fields, id for the first field is "dateFrom" and the other field is "dateTo". Next to that is a "Go" button. Upon clicking the button, a JS function shows an alert box with the values in each of the input fields.
Now, I click on the "Traffic Data2" tab. The contents of the page are very different, but it has the identical input fields, 2 for dates (with same IDs) and Go Button. When I press the Go button on this page, I see the alert box with values form the previous tab.
So my question is, Is there a way to unload the previous tab? Or is the only alternative to use elements with unique divs (even though the pages are complete separate).
Thanks
You cannot have multiple element with the same ID. When you find an element by ID the first one found is always returned because it is expected that IDs will be unique.
Leave the name attributes for the input elements the same but change their IDs to be unique.
You can select an element by its name attribute like this: $('[name="foobar"]')
Update
In the docs for jQuery UI Tabs there is an option called cache that when set to false should remove the old tabs from the DOM after you navigate away from them:
Whether or not to cache remote tabs content, e.g. load only once or
with every click. Cached content is being lazy loaded, e.g once and
only once for the first click. Note that to prevent the actual Ajax
requests from being cached by the browser you need to provide an extra
cache: false flag to ajaxOptions.
Source: http://jqueryui.com/demos/tabs/
You are looking for jQuery Live.
Description: Attach an event handler for all elements which match the current selector, now and in the future.
If you use it jQuery will magically auto-update to match your new elements as they appear/disapear.
// code sample
$("a.offsite").live("click", function(){ alert("Goodbye!"); });
Since the tabs click event reloads your form and assuming you're using divs to contain the ajax-loaded content, add .click(function () { $(this).children('div').children.remove(); }) to your tabs() declaration.
That should remove the div content and any event handlers that are bound to it.
If you can pass a context to the jquery functions, you could make your calls relative to currently selected tab...
$("#someDuplicatedId", activeTab).doStuff();
Or if caching the content is not important, go with Jasper's answer.

Categories