Apologies for the long question, but I've tried a lot of things and done some research and havent found much of a solution. I have a content editable div.
<div contentEditable=true onkeyup='showResult(this.lastChild.textContent)'></div>
When a user types something into this div, the showResult javascript runs which is basically an ajax request which returns a list of items that match. When a user clicks on one of the suggestions, say the name "John", a span with the suggestion is added into this contentEditable div like so:
<div contentEditable=true onkeyup='showResult(this.lastChild.textContent)'>
<span id='uniqueId1' class='SpanClass' contentEditable='false'>John</span>
</div>
Having selected one Name, the user may want to search for another name. It HTML terms, that means that they would be typing the following:
<div contentEditable=true onkeyup='showResult(this.lastChild.textContent)'>
<span id='uniqueId1' class='SpanClass' contentEditable='false'>John</span>
New User Text Goes Here
</div>
On Chrome, the right behaviour happens when the user continues tries typing in the div - the showResult function runs on the new text that the user types in and ignores the span elements. For Example, if the user types in "Fr" having already selected John, it ignores the first children (John), and sends what the user typed off via ajax and returns suggestions like Fred and Frankie.
However, in IE the span is still content editable and the user can't add any text other than within the span, which seems to make no sense as it is clearly contentEditable=false The ajax request is therefore run on the "John" text plus whatever the user types in next, which is not what I'm trying to achieve.
Finally, in Firefox, the span is not contentEditable BUT the lastChild bit only picks up text within the span, and ignores the text the user puts in.
I've console logged the results of showResult(this.lastChild.textContent) to see what is being sent to the ajax request.
In Chrome, typing in "Fr" in the box after the "John" span sends "Fr" to the ajax and returns the right result.
In IE, typing in "Fr" in the box sends "JohnFr"
In Firefox, typing in "Fr" just sends "John".
As the issue is with this lastChild and the span, I've also included the Javascript that creates the span element. This only activates after a successful result is return and the result is clicked on. (please excuse the very messy Javascript/Jquery)
$('body').on("click", '.TagHints', function(){
//Once you click on the suggestion
var ThisData = $(this).data("id");
var ThisId = $(this).attr("id");
var ThisTag = $(this).data("tag");
//delete the text that the user typed in
elementToRemove = document.getElementById("FakeInput").lastChild;
document.getElementById("FakeInput").removeChild(elementToRemove);
var TagDiv = document.createElement('span');
TagDiv.className = 'SpanClass';
TagDiv.id = ThisId;
TagDiv.innerHTML = ThisTag;
TagDiv.contentEditable=false;
//append the Span to the contentEditable div
document.getElementById("FakeInput").appendChild(TagDiv);
var TagHints = document.getElementsByClassName("TagHints");
while(TagHints.length > 0){
TagHints[0].parentNode.removeChild(TagHints[0]);
}
});
Why are the three browsers behaving completely differently and how do I get them all to behave like Chrome is? Is there a better way of getting the text not in the spans?
I read on another answer that firefox likes inputs and IE likes breaks in this context but both do not seem to work for me. :-(.
One big stopper to good solutions is that jQuery stops working after about line 6, which has also completely baffled me. If anyone can explain why its not working, that would also be cool. Maybe something to do with it being an ajax query and content being created after jquery is loaded?
Thanks for your help!
So, although I haven't had any responses I've come up with a workaround. However, lastChild is still behaving differently between the three browsers so would appreciate any further input.
The answer was to have a function in the document ready section which cloned the original div, stripped it of its children and grabbed the text on key up using Jquery. This then passed the result to the showResult function like so:
$('#FakeInput').keyup(function(){
var ThisClone = $('#FakeInput').clone()
.children()
.remove()
.end()
.text();
showResult(ThisClone);
});
Equally, rather than trying to remove the text that the user typed in from the divs when the user makes a selection I simply cloned the items in the span, using their class, then emptied the contents of the div and reappended these cloned divs.
var TagItems = $('.TagItems').clone();
$('#FakeInput').empty();
$('#FakeInput').append(TagItems);
This solution works across the three browsers.
Related
I am developing a WebExtension, and one feature is to have a context menu item on editable fields, which when selected opens a confirmation window and then pastes a value into the editable field.
At the moment I have the context menu item, which opens the window, but getting some text inserted into the editable field is proving tricky. The closest I have managed so far is:
let code = 'setTimeout(function() {document.designMode = "on";document.execCommand("insertText", false, "apple");document.designMode = "off";}, 1000);';
browser.tabs.executeScript(parent_tab_id, {"code": code});
window.close()
The whole designMode thing seems it bit weird, and the code does not work very reliably. Is there a better way to do this? The root of the problem, is that I don't see any way to find the editable field that was clicked on.
I would do it like this:
let code = 'document.activeElement.value = "apple";';
browser.tabs.executeScript(parent_tab_id, {"code": code});
By the way, window.close inside a background script is browser.tabs.remove(currentTabId). You can get the current tab id by querying the tabs API (example 2): https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/query#examples
My apology as this question is quite long.
To be more specific, I am trying to click on the Pencil icon (Edit button) that is shown on the 4th row
Here's the code I tried to execute:
WebElement ele = driver.findElement(By.xpath("//a[contains(#title, 'Edit Row')]"));
JavascriptExecutor executor = (JavascriptExecutor)driver;
Executor1.executeScript("arguments[0].click();", ele);
And here's the HTML code
After I ran the selenium code, the driver actually clicked on the Edit button of the 1st row (Administration | NA) instead of the 4th. I found out that not only the 1st but other rows also have the same HTML code, only difference was that the Pencil/Edit button is hidden because I wasn't the one that created the values of that row.
I did try with the following code as well:
driver.findElement(By.xpath("//a[contains(#title, 'Edit Row')]")).click();
I then received the error
element is not currently visible and so may not be interacted with
It seems the driver was still looking at the hidden Edit button in the first row, instead of the obvious visible one in 4th.
Is there anyway that I could click on the Edit button based on the value instead of the row number? The reason is that in the future, the value I enter for testing may not end up in 4th but 1st or 10th row.
Last but not least, my Safari can't seem to run the JS Executor code, everytime I gave it a go, the following error returned:
org.openqa.selenium.WebDriverException: 'undefined' is not a function (WARNING: The server did not provide any stacktrace information)
Can someone shed some light on this for me as well?
Cheers
Since there is only one edit icon displayed, the code below should work.
driver.findElement(By.cssSelector("a[title='Edit Row']")).click();
I think the reason you were running into the hidden element issue is that you were using JSE to click on it. Selenium is designed to only interact with elements that are visible... the way a user would. JSE allows you to interact with elements that are NOT visible. There are reasons to use JSE but I don't believe this is one of them. Using JSE you were finding an element that matched your XPath that was not visible and then trying to click on it... thus the error.
I suggest you to go once in firefox and then try same xpath in firebug with firepath add-on.
Try to inspect xpath of the pencil icon. Only one maching element with no dynamic values in that, then you can and use it. Else evaluate this xapth
//a[contains(#title, 'Edit Row')]
You can find one or more matching elements. Then try to find difference in one of attribute for required element. If you are able to create xpath by using that odd attribute then its on else go for List and in loop cross theck for that, then work on it.
Thanks murali and JeffC for the suggestions, I have managed to get the driver to click on the displayed Pencil with this:
List<WebElement> EditButtons = driver.findElements(By.cssSelector("a[title='Edit Row']"));
for(WebElement button : EditButtons) {
if(button.isDisplayed()) {
button.click();
}
}
Although it only solved half of the issue, I am trying to click on the displayed Edit button based on the value next to it. If I create another row with the value of 'Test234' and if the row is placed under the 4th row, the Edit button of 'Test234' won't be clicked. Any suggestion? I tried this but it doesn't seem to work, driver still clicked on the first visible edit button it saw:
List<WebElement> EditButtons = driver.findElements(By.cssSelector("a[title='Edit Row']"));
String textvalue = driver.findElement(By.xpath("//span[contains(text(),'test456')]")).getText();
for(WebElement button : EditButtons) {
if(button.isDisplayed() && textvalue.equalsIgnoreCase("test456")) {
button.click();
}
}
UPDATE: Alright, issue resolved, I switched back to use xpath instead of cssSelector, then modified Vagnesh's suggestion (Thanks Vagnesh !!) a bit and now the driver is clicking on the visible edit button next to the text I search for without going through the loop
driver.findElement(By.xpath("//span[contains(text(), 'Enter my desired Text here')]/following::div//a[contains(#title,'Edit Row')]")).click();
You should find the element based on the field text, so that it can find its corresponding edit link.
functional_area = "Business Development / Innovation"
WebElement ele = driver.findElement(By.xpath("//span[#class='xspTextComputedField'][contains(#text, '" + functional_area + "')]/following-sibling::div/a[contains(#title, 'Edit Row')]"));
You can make it more dynamic by parameterizing the text ex. Financial.
I'm trying to create a textarea that listens to URLs typed, pasted or dropped and converts them to links. These links are not editable and must be treated as blocks. Delete and backspace must delete them. Finally, it would be nice if they show full selection when the caret is moved through them.
Problems: after spending too much time and trying some of the suggested solutions found here on stackoverflow (Tim Down), I still couldn't get it to work correctly. I'm finding it very hard to have the caret in the correct place all the time and after pasting a link and the automatic conversion is done, the selection is locked and no input can be done. I tried to insert and keep a zero width character before and after each link but then other selection problems started to occurr.
Can someone please have a look at the following fiddle which represents a simplified version focusing the problem and point me to the right direction for the correct keyboard navigation through these non-editable links inside a contenteditable div?
jsFiddle here
$("#customtextarea").on("input propertychange drop paste", function (e) {
var $this = $(this), savedSelection = saveSelection($this.get(0));
var parsedHtml = getParsedHtml($this.html()); // This gets the innerhtml with the urls turned to links
$this.html(parsedHtml).focus();
restoreSelection($this.get(0), savedSelection);
});
EDIT: I've taken the approach suggested by XuoriG and I'm still facing more or less the same issues as before: caret gets 'stuck' after the link and the links can't be deleted. Also keyboard navigation (left, right) is not working.
New jsFiddle here
When using $this.html you're replacing the entire DOM and losing your selection.
The way I was able to do this is by creating and removing DOM nodes on the fly on every key up. I can't post code for you right now but basically you go through every node on key up and create a new anchor tag with the content matched by your url regex and remove that from text node. Repeat until you dont have any more matches in the node. You'll be able to save and restore the selection this way.
The hard part after is being able to append to an existing url. The way I did it is by detecting if a text node is right next to an anchor tag, and if the anchor text + the text node matches an url too you append the text node to the anchor tag and remove the text node.
I have set a bookmark which has -adult word to search query, so that inappropriate results won't show up. Now, how do I hide -adult present in the search box?
Example:
My idea so far:
To use CSS to hide it then make another textbox to be seen, then add a few event handlers to sync the real search box with my fake search box.
comments by #wOxxOm
that's tricky to implement I suppose since google uses ajax and
events, but probably it'll be possible to set a document mutation
observer which will hide the stuff in the search box before it's shown
If the above method is not possible, then please suggest alternative for solving this problem.
Remove -adult from the field and append it on the button click event. Example:
var keywordInput = $('input[name="-adult"]');
$('input[name="SearchButton"]').click(function() {
window.location = 'http://example.com?kwd=' + encodeURIComponent(keywordInput.val());
});
dont put the word you dont want them to see... use this code instead:
<?php
$dasearchstring = "I Want this text hidden";
$darealsearchstring = "Test".$dasearchstring;
?>
js code:
var dasearchstring = "I Want this text hidden";
var darealsearchstring = "Test"+dasearchstring;
dasearchstring is what the user searched; darealsearchstring is what is really searched.
The text "Test" is the string that you want to prefix searches with.
hope this clears it up!
(document.getElementById('textarea').length > 0) doesn't work. Does anyone know anything else other than this?
Will
Here is the scenario from my previous question which was unanswered. I have Rich text Editor(Openwysiwyg) which is loaded into textarea when I go to that particular page where textarea is placed. The function uses textarea id to identify textarea to replace it with Rich Text Editor(RTE). Now the script to call this function is in header part of the page. I select a drop-down option for sending email, so my textarea for email shows up. With this script added for RTE, my textarea for email is replaced by RTE and I can send formatted emails. So this works perfectly fine in Firefox. With IE7, RTE shows up even before I select drop-down option for email and this makes whole page messed up.When I select drop-down option for email, I just see normal text area and RTE still sitting at top of page.
document.getElementsByTagName('textarea').length > 0
You can use (note the plural form!)
var e = document.getElementsByTagName("textarea");
You can use jQuery as well:
$("textarea").each( function() { /* ... */ } );
EDIT:
I faced a similar problem once. I was using fckedit, and when I tried reading the value of my textedit (document.getElementById('blabla').value) I was getting null, even tough the rich text edit was ddefinetly showing something on screen.
It turns out that the fckedit API opens a new element on top of the textearea, and only when you navigate from the page is syncs it's internal data (which is on an iframe, if I am not mistaking) into the original textarea.
The moral of the story: if you are using some richtext API - use it's API to query the status of your "textarea". Hope this helps you, as I don't know the library you are using.
PS: I actually used $("blabla").val() ... which is also JavaScript... for some reason people think that jQuery is not javascript. Why is that?
Pure JavaScript (You can use this code)
textarea is HTML element tag
JavaScript :
if(document.getElementsByTagName('textarea').length > 0) {
}
HTML CODE:
<div class="flavor">
<div class="value">
<textarea name="name" rows="8" cols="80"></textarea>
</div>
</div>
<script type="text/javascript">
var flavorbox = document.getElementsByClassName('flavor')[0]
.getElementsByClassName('value')[0]
.getElementsByTagName('textarea').length;
alert(flavorbox);
</script>
Since openWYSIWYG generates a iframe on the fly, its not so simple to get/set its content.
I am currently working on changing these settings in the source. will post here a link as soon as i get the changes.