I want to automate canva.com site, using python's selenium library.
What I need to do ?
I need to insert data to fields inside canva (very simple). To force canva save my changes I need firstly DoubleClick the element, and only then update the text.
What is my problem ?
After I double click the element, it's changing ... and I can't change that python element's text. to change it, I need to find that element again and only than change it. - I Got an error that the element is not attached to the page.
def get_report_params_fields(driver, keys):
fields = {}
all_paragraphs = driver.find_elements_by_tag_name("p")
for paragraph in all_paragraphs:
key = re.sub(' +', ' ', paragraph.text.upper()).split(":")[0]
if key in keys:
if key in fields.keys():
fields[key].append(paragraph)
else:
fields[key] = [paragraph]
return fields
# update fields list
canva_fields = get_report_params_fields(driver)
# click the field to change
canva_fields[canva_key][task_index].click()
canva_fields[canva_key][task_index].click()
# update fields list
canva_fields = get_report_params_fields(driver)
# update the data
value = key + ": " + keys[key]
driver.execute_script("arguments[0].textContent = arguments[1];", canva_fields[canva_key][task_index], value)
The problem is that the get_report_params_fields (which just goes over all the paragraphs on the page and select the one I need) function is not efficient, so I want to use it only once.
So I am searching for a way to get the webelement I click using another way (using it's xpath, location, some other unique id of the webelement). I mean I need an option to find the same element again using the webelement properties, Without known the properties before (like checking the xpath in chrome, and I can't use the element's text because it is changing from time to time).
I thought about double clicking and changing the text inside oneliner javascript script, but I didn't found a way to do that.
Related
how to make an input like on the google site? I need that after entering the texts and in the input, Google opens with the desired request.
on JAVASCRIPT
google's input
1
2
my input(html)
<input class="finder" type="search" placeholder="Search in Google">
You can add an event listener to let's say a button.
When that button is clicked, the value in your search field will be added to the end of a Google search link.
However, since you can expect people to type sentences (and urls don't use spaces), you can use replace() to replace all spaces with, in our case, a + (url format for spaces is %20, but spaces in queries are commonly replaced with a + character).
Lastly, you can use window.open(), to open a new window that uses our link.
UPDATE: You can use a keypress as a trigger for the eventlistener, and then check if the entered key is an "Enter", that way you don't need a button.
Here's an example:
SEARCH_GOOGLE_VALUE is the textfield the user can type
const SEARCH_GOOGLE_VALUE = document.getElementById('SEARCH_GOOGLE_VALUE');
SEARCH_GOOGLE_VALUE.addEventListener("keypress", (keyboard) => {
if (keyboard.key == "Enter") {
let value = SEARCH_GOOGLE_VALUE.value;
value = value.replace(' ', '+');
let link = "https://www.google.com/search?q=" + value;
window.open(link);
}
});
if you mean redirect to a google page after input this would work
document.querySelector('.finder').addEventListener('focusout',function(){
location.replace("https://www.google.com/search?q=" + document.querySelector('.finder').textContent);
});
this will send the user to a google page with the needed requests when they leave the input fild
if you need to make something simular but on your oun website the best option would be to send the user to a nother page with the query data in the url and some back end to construct and return a needed page.
or you could send a fetch and get info back on focus out
The website I am interacting with uses shadow DOMs. I can successfully interact with them for clicking buttons and in some cases can input text but I am currently stumped on this one.
The HTML looks like this
firefox html inspector view of one shadow dom input text box
I can input in the text fields with the following but the continue button doesn't enable for me to go to the next screen and if I manually click and place a space in the first box I get an error saying that to continue to the next page I need to fill in the second and third boxes (which have text but for some reason the webpage doesn't recognize that text is present). table with shadow dom inputs
shadowDOM1 = driver_local.execute_script("return document.querySelector('WN-input').shadowRoot.querySelector('input')")
str_valToSet = "arguments[0].value = '" + partNum + "';"
driver_local.execute_script(str_valToSet, shadowDOM1)
shadowDOM2 = driver_local.execute_script("return document.querySelectorAll('WN-input')[2].shadowRoot.querySelector('input')")
str_valToSet = "arguments[0].value = '" + str(num2buy) + "';"
driver_local.execute_script(str_valToSet, shadowDOM2)
shadowDOM3 = driver_local.execute_script("return document.querySelectorAll('WN-input')[1].shadowRoot.querySelector('input')")
str_valToSet = "arguments[0].value = '" + "CUSTX" + "';"
driver_local.execute_script(str_valToSet, shadowDOM3)
I was also able to click the text box input that is in the shadow root but when i go to send keys i get an error that "element is not reachable by keyboard". There is not a temporary overlay preventing me from typing, as far as i can tell there is not a permanent overlay, and I see no evidence of any attributes that would make it invisible to selenium (org.openqa.selenium.ElementNotInteractableException: Element is not reachable by keyboard: while sending text to FirstName field in Facebook)
fields = driver.find_elements(By.XPATH, '//tbody/tr/td/WN-input')
print("fields", fields)
print("field", fields[0])
shadowEl = expand_shadow_element(driver_local, fields[0])
print("shadowEl", shadowEl)
isVisible = WebDriverWait(shadowEl[1], maxDelay).until(EC.visibility_of_element_located((By.XPATH, "//input")))
print("isVisible", isVisible)
shadowEl[1].click()
time.sleep(2)
shadowEl[1].send_keys("blahblah")
In a different location within my code I successfully interact with a similar object this this (WN-input tag name) and was able to get it to open up the "next" button by typing some garbage text in a different input field that wasn't within a shadow DOM. When I try that technique for this case it blows away my text within my shadow DOM text input fields. I am not sure why this case seems to work and the case in question doesn't.
shadowDOM1 = driver_local.execute_script("return document.querySelector('WN-add-to-cart').shadowRoot.querySelector('WN-input')")
str_valToSet = "arguments[0].value = '" + str(num2buy) + "';"
driver_local.execute_script(str_valToSet, shadowDOM1)
try:
inputBox = WebDriverWait(driver_local, maxDelay).until(EC.element_to_be_clickable((By.XPATH, "//input[#placeholder='Search']")))
#inputBox = local_driver.find_element_by_xpath("//input[#placeholder='Search']")
inputBox.click()
inputBox.send_keys("text")
time.sleep(3)
After days of failed attempts and google search after google search I am finally ready to ask for help. Any thoughts?
I have created a task pane for word 2016 which has two buttons such as 'addcontentcontrol' and 'retrievecontentcontrol' . Adding a content control in document works fine.When i select that content control's text and hit 'retrievecontentcontrol',it returns text. However, I want to check whether the selected text contains content control or plain text. Thanks a lot in advance.
I think you are asking about two things. If you are selecting a content control's text, and want to return the content control, then you'll want to do the following:
You'll want to check the range.parentContentControl property to check whether the selected text is within a content control. If the returned value is not null, then you may want to compare the text value of the content control and the text value of the selected range to make sure they are equivalent.
var contentControl = context.document.getSelection().parentContentControl;
But if you want to check whether some arbitrary text from a selection contains a content control, then you'll want to check the content control collection on the range.
var contentControlCollection = context.document.getSelection().contentControlCollection;
Maybe that happens because you are not loading the content control before calling context.sync()? ... try this code it must work (note that we get a GeneralException if there is no content control in the selection). Note that This sample assumes that if there is a content control it has a title on it :)
function insideOfContentControlCheck() {
Word.run(function (ctx) {
var myCC = ctx.document.getSelection().parentContentControl;
ctx.load(myCC); // I think this is the part you are missing!
return ctx.sync()
.then(function () {
console.log(myCC.title);// if there is a content control we'll show the title
});
}).catch(function (e) {
//there is no ContentControl.
console.log("Error", e.message);
});
}
My aspx page contains list of products along with dynamically generated textboxes and one order button with each product.
Textboxes and buttons are generated at runtime with ids like txt110234,txt110235...so on for textboxes and btn110234,btn110235...so on for buttons.
Each time user have to enter quantity in the textbox and press order button associated with any product to place any order.
Every thing is working fine but now i want to do it using ajax,so i need to get the value entered by user in text box.I want to do something like this-
var quan = document.getElementById('<%= txt' + id + '.ClientID%>').value;
But its giving me the following error.
Compiler Error Message: CS1012: Too many characters in character literal Source Error:
How can i get the value of textbox?Any suggestion will be appreciated..
The error you got is because you can't involve javascript inside the "<%= .. %>" block. Also this doesn't look possible since the "<%= .. %>" expression is evaluated in server before the page is rendered, but your "id" is a client side variable.
You can set the script in server side like that:
client side code:
function foo(ctlID)
{
var quan = document.getElementById(ctlID).value;
}
server side code:
TextBox txt = new TextBox();
txt.ID = "SomeID";
Form.Controls.Add(txt);
Button btn = new Button();
btn.ID = "someID";
btn.OnClientClick = "foo('" + txt.ClientID + "')";
Suggestion:
One way of doing this is to use jQuery css selector. You can assign a particular cssclass to all your input textbox and retrieve all of them via jQuery selector.
For example, on generating textboxes dynamically, you can assign them CssClass =".productQuantity"
and then later use jQuery selector something like $('.productQuantity')
I personally prefer this approach If I would like to traverse to multiple elements. This saves me from dealing with Ids etc.
What I am trying to achieve is to force a textbox to start with a prefix ( country telephone code ) and to make this permanent, meaning that the user cannot bypass this. For example, the Phone textbox should always start with "+45" and after that the user can add the phone number. How to prevent it from deleting the code, by any means?
What I done so far, using jQuery:
//attach event on phone text boxes
$(document).delegate(".phone", "keyup", function(event){
var target = $(this);
var value = target.val().trim();
if (value.indexOf(CONSTANTS.DANISH_PHONE_CODE) == -1) {
//country code not found
//if the user starts deleting the country code
if (value.indexOf("+") == 0){
value = "";
}
//if the user types something in front of the country code, put the country code at the end
value = value.replace(CONSTANTS.DANISH_PHONE_CODE, "");
//phone doesn't start with +45
value = CONSTANTS.DANISH_PHONE_CODE + value;
target.val(value);
}
});
But the problem is that the user can delete the plus sign and the prefix is put automatically at the start so we will have +4545. Do you know an elegant way of achieving this? Thanks.
You can absolutely position the text (in a span) over the textbox and add a left-margin to it.
This way users won't be able to remove it. But you'll have to add it server side.
Add the +45 as static html before the field. Required the user to enter "the rest" of the number (not the +45).
If necessary, add the +45 server side before persisting the value. Similarly, remove the +45 when editing.
JSFiddle Example
This should actively keep them from deleting "+45" instead of trying to fix the problem after the user as changed it. Upon keypress, determine character position, if the position is too early in the string (i.e. inside the "+45" as oppose to after it) then don't allow the keypress, unless that key is the left or right arrow keys.
Acquired resources:
http://blog.vishalon.net/index.php/javascript-getting-and-setting-caret-position-in-textarea
Binding arrow keys in JS/jQuery