I can simulate text selection with protractor by a lot of different ways. But I can't find solution how I can expect that text was really selected. How I can do this without using marking?
I can simulate select text by protractor with:
DragAndDrop function (mouse)
Combination: browser.actions().sendKeys(protractor.Key.CONTROL, 'a').perform();
Combination: shift + left arrow
Elaborating Michael's comment into a working example.
In the following demo example, we are navigating to the AngularJS website, sending "testing" text to the search input on the top right, pressing left arrow 3 times to move the cursor 3 characters to the left, then pressing SHIFT + ARROW_RIGHT keyboard combination 2 times to select the next 2 characters - which is in:
Finally, we are applying the solution to get the selection text provided here and asserting:
describe("Get selection text", function () {
beforeEach(function () {
browser.get("https://angularjs.org/");
});
it("should input 'testing', select 'in' and assert it is selected", function () {
var q = element(by.name("as_q"));
// enter 'testing' and go 3 chars left
q.sendKeys("testing")
.sendKeys(protractor.Key.ARROW_LEFT)
.sendKeys(protractor.Key.ARROW_LEFT)
.sendKeys(protractor.Key.ARROW_LEFT);
// select 2 chars to the right
browser.actions()
.keyDown(protractor.Key.SHIFT)
.sendKeys(protractor.Key.ARROW_RIGHT)
.sendKeys(protractor.Key.ARROW_RIGHT)
.keyUp(protractor.Key.SHIFT)
.perform();
// get highlighted text
var highligtedText = browser.executeScript(function getSelectionText() {
var text = "";
if (window.getSelection) {
text = window.getSelection().toString();
} else if (document.selection && document.selection.type != "Control") {
text = document.selection.createRange().text;
}
return text;
});
expect(highligtedText).toEqual("in");
});
});
Hope this would help you and others coming here.
Here you might also need to verify that the text inside a particular text box is selected. You can do that by using these 2 lines below:
*document.evaluate( 'XpathOfTheElement//input', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue.selectionStart
document.evaluate( 'XpathOfTheElement//input', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue.selectionEnd*
if 'in' is selected in the string 'testing' then first line will return 4 and second line will return 6.
Related
I have been trying to insert emoji in textarea exactly where the cursor is at. I looked around how tos in the web could not find anything specific in VUE JS. Most of them are in plain JS.
I have this Code
<div class="picker" v-show="showPicker">
<click-outside :handler="handleClickOutside">
<picker
set ="messenger"
title="Pick your emoji…"
emoji="point_up"
#click="addEmoji"
:emoji-size="16"
>
</picker>
</click-outside>
</div>
<textarea id="greeting_text_input" class="form-control"
type="text"
v-model="greeting_text"
rows="8"
required
placeholder="Hi {first-name}! Welcome to our bot. Click on the ‘Get
Started’ button to begin
">
</textarea>
My Method
addEmoji(emoji){
this.greeting_text += emoji.native;
this.showPicker = !this.showPicker;
}
Obviously, this code will add the character (emoji, in my case) to the last of the string. I need a pure vuejs solution for this.
What would be the best practise for this kind of problem in Vue? as there are few solutions in the web that based either in vanilla JS or Jquery.
Two steps:
1 get textarea element using a vue-way:
1.1 Add ref attrbute to textarea tag in your template code:
<textarea ref="ta"></textarea>
1.2 get this element after mounted hook of this component:
let textarea = this.$refs.ta
2 get cursor position of textarea element.
let cursorPosition = textarea.selectionStart
Here is reference: ref
<!-- tag -->
<textarea ref="yourTextarea" v-model.trim="txtContent" ......></textarea>
// methods:
insertSomething: function(insert) {
const self = this;
var tArea = this.$refs.yourTextarea;
// filter:
if (0 == insert) {
return;
}
if (0 == cursorPos) {
return;
}
// get cursor's position:
var startPos = tArea.selectionStart,
endPos = tArea.selectionEnd,
cursorPos = startPos,
tmpStr = tArea.value;
// insert:
self.txtContent = tmpStr.substring(0, startPos) + insert + tmpStr.substring(endPos, tmpStr.length);
// move cursor:
setTimeout(() => {
cursorPos += insert.length;
tArea.selectionStart = tArea.selectionEnd = cursorPos;
}, 10);
}
I learned about setSelectionRange from a different question, and I used it to handle credit card number input. I will show my solution here so a person can perhaps become inspired by it.
template:
<input
ref="input"
v-model="value"
#input="handleChange"
>
instance methods:
data() {
return {
lastValue: '',
}
},
methods: {
setCursorPosition(el, pos) {
el.focus();
el.setSelectionRange(pos, pos);
},
handleChange() {
// handle backspace event
if (this.value.length < this.lastValue.length) {
this.lastValue = this.value;
this.$emit('input-changed', this.value);
return;
}
// handle value-edit event
if (this.$refs.input.selectionStart < this.value.length) {
const startPos = this.$refs.input.selectionStart;
this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
this.$nextTick(() => this.setCursorPosition(this.$refs.input, startPos));
this.lastValue = this.value;
this.$emit('input-changed', this.value);
return;
}
// handle everything else
this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
this.lastValue = this.value;
this.$emit('input-changed', this.value);
},
},
The goal with the above code is to add spaces into a credit card input, so 1234123412341234 is automatically reformatted to 1234 1234 1234 1234. A person venturing into this territory will notice that problems arise when editing the input value.
You can see there are three conditions in my sample above. The last one is the default which simply reformats the current value with a 2-step combo: remove all spaces then adds a space every 4th character.
If you comment out the two if blocks, you can watch the problems emerge.
The first if block handles the backspace event. As you can see, every time the input changes, the value is captured as this.lastValue. When you press backspace, the goal of the first condition is to NOT run the regex. In my opinion, this is better UX. If you comment out that condition, you can see.
The second if block handles the editing events. A good way to test it, is to enter a valid CC but omit the 3rd character, so that everything is off by one. Then add the character in. Everything should be good. Likewise if you backspace multiple characters out. The goal of the second condition is to properly manage the cursor position (or caret position if you prefer that nomenclature).
You can safely delete the first condition and all references to lastValue and the code will still work. This is arguably simpler but worse UX.
JSfiddle for reference: https://jsfiddle.net/9jp346r4/20/
I am trying to create functionality that allows user to highlight the selected text upon pressing a button, and unhighlight the highlighted text upon right-clicking.
I've gotten it mostly working using the rangy library except there's one scenario that doesn't work and I'm not sure how to solve it.
When I highlight text that is in 2 different paragraphs, it highlights it successfully.
The issue arises when I would like to come back later and un-highlight both the paragraphs.
The expected behaviour is: I right-click any highlighted text regardless of if it is selected or not and it will un-highlight all nearby highlighted text even if it's separated by a paragraph tag or strong tag.
The current behaviour is: It only unhighlights the text in the paragraph I clicked.
To re-produce:
1) Select text that overlaps both the first and second paragraph and press the "Press" button.
2) Un-select the selected text by clicking somewhere else on the screen.
3) Right-click any of the highlighted text. Notice only one of the paragraphs gets un-highlighted.
If something is unclear, feel free to ask questions. Would appreciate the help.
Here is my HTML:
<div id="content">
<p>
Paragraph 1
</p>
<p>
Paragraph 2
</p>
</div>
<div id="divId">
<input id="myBtn" type="button" value="Press" onclick = "javascript:toggleItalicYellowBg()"/>
</div>
Here is my javascript:
function coverAll() {
var ranges = [];
for(var i=0; i<window.getSelection().rangeCount; i++) {
var range = window.getSelection().getRangeAt(i);
while(range.startContainer.nodeType == 3
|| range.startContainer.childNodes.length == 1)
range.setStartBefore(range.startContainer);
while(range.endContainer.nodeType == 3
|| range.endContainer.childNodes.length == 1)
range.setEndAfter(range.endContainer);
ranges.push(range);
}
window.getSelection().removeAllRanges();
for(var i=0; i<ranges.length; i++) {
window.getSelection().addRange(ranges[i]);
}
return true;
}
function getSelectedText() {
if (window.getSelection) {
return window.getSelection().toString();
} else if (document.selection) {
return document.selection.createRange().text;
}
return '';
}
var italicYellowBgApplier;
function toggleItalicYellowBg() {
italicYellowBgApplier.toggleSelection();
}
window.onload = function() {
$(document).on("contextmenu", ".italicYellowBg", function(e){
if(coverAll()) {
italicYellowBgApplier.undoToSelection();
return false;
}
});
rangy.init();
// Enable buttons
var classApplierModule = rangy.modules.ClassApplier;
// Next line is pure paranoia: it will only return false if the browser has no support for ranges,
// selections or TextRanges. Even IE 5 would pass this test.
if (rangy.supported && classApplierModule && classApplierModule.supported) {
italicYellowBgApplier = rangy.createClassApplier("italicYellowBg", {
tagNames: ["span", "a", "b", "img"]
});
}
};
I guess to easy solve this problem, in memory keep an array of user highlights, one item of that array is not a single highlight item, but a further "selection of selected items during the highlight", when somebody would right click on a single segment of highlight, in-memory find from array all associated highlights and unhighlight the related segments by yourself.
Following uraimo's great response here I have managed to create a handler for making the text bold:
addHandler('bold', function (obj) {
var isBold = getStyle(obj, 'fontWeight') === 'bold';
setStyle(obj, 'textDecoration', isUnderline ? '' : 'underline');
});
It is attached to the button and it works fine whenever there is a portion of the text selected.
However, I need to also react on the button clicks when there is no text selected. Clicking a button in such case would result in bold text that is subsequently typed (just like hitting the "bold button" works in any text editor).
How can I do that?
You can bold text that is subsequently typed and also the one applied by setting the preperty of text
addHandler('bold', function (obj) {
var isBold = getStyle(obj, 'fontWeight') === 'bold';
var style = {};
style['fontwieight'] = isBold;
object.setSelectionStyles(style);
object.set('fontweight', isBold);
});
I may be going in the completely wrong direction with what I'm trying to do, so I wanted to ask for help.
Background / Overview
I need to display a paragraph of text and allow a user to select one or more words from the paragraph and save their highlighted text to a database, for just their profile. Actually, hat selection of text will eventually be (1) stored with the highlight AND (2) linked up to another set of highlighted text from another paragraph (basically, I'm tying a phrase from one source to a reference source)
What I've tried...
I have tried to put each word of the paragraph into a DIV (and a unique ID) with each DIV set to float left, so that the display looks okay.
<style>
div { float: left}
</style>
and...using an example:
<div id="GEN_1_1">
<div id="GEN_1_1_1">In</div>
<div id="GEN_1_1_2">the</div>
<div id="GEN_1_1_3">beginning</div>
<div id="GEN_1_1_4">God</div>
<div id="GEN_1_1_5">created</div>
<div id="GEN_1_1_6">the</div>
<div id="GEN_1_1_7">heaven</div>
<div id="GEN_1_1_8">and</div>
<div id="GEN_1_1_9">the</div>
<div id="GEN_1_1_10">earth</div>.
</div>
Which looks like: In the beginning God created the heaven and the earth. (minus the bold)
So far, I have used the
window.getSelection()
function to determine/grab the words that have been highlighted.
I then tried using this:
if (window.getSelection)
{
selected_len = window.getSelection().toString().length;
if (window.getSelection().toString().length>0)
{
div_id = window.getSelection().getRangeAt(0).commonAncestorContainer.parentNode.id;
}
}
to get the ID's for each DIV selected, BUT I only get a single DIV ID returned right now.
Help Request:
Is there are slick way to get the ID for each DIV selected and put it into an Array, so that I can construct a SQL query to put it into the database (the query is easy)? The selected words could total up to several hundred, if not a thousand words, so I need to make sure the solution will work with a ton of words selected.
UPDATE: JSFIDDLE DEMO
I modified the code again. See if it works for you now.
$(document).ready(function () {
$(document).on('mouseup keyup', '.checked', function () {
console.log(window.getSelection());
if (window.getSelection().toString().length>0) {
var count = window.getSelection();
var arr = [];
$('.checked span').each(function(){
var span = $(this)[0];
var isT = window.getSelection().containsNode(span, true);
if(isT){
arr.push($(this).attr('id'));
}
});
console.log(arr,count.toString());
alert(arr);
alert(count.toString());
}
});
});
I created a fiddle for solution. Check it out here: http://jsfiddle.net/lotusgodkk/GCu2D/18/
Also, I used span instead of div for the text selection. I hope that won't be an issue for you. So the code works as you want. It will return the id of the parent span in which text is selected. You can modify it to save the ID into array or as per your needs.
$(document).ready(function () {
$(document).on('mouseup', '.checked', function () {
if (window.getSelection) {
var i = getSelectionParentElement();
console.log(i);
alert('parent selected: ' + i.id);
}
});
});
function getSelectionParentElement() {
var parent = null, selection;
if (window.getSelection) {
selection = window.getSelection();
if (selection.rangeCount) {
parent = selection.getRangeAt(0).commonAncestorContainer;
if (parent.nodeType != 1) {
parent = parent.parentNode;
}
}
} else if ((selection = document.selection) && selection.type != "Control") {
parent = selection.createRange().parentElement();
}
return parent;
}
I am trying to get the selected texts from the user(the highlighted text that user highlights).
I have the following:
function getSelectedTexts(){
var t = '';
if(window.getSelection){
t = window.getSelection();
console.log('1');
}else if(document.getSelection){
t = document.getSelection();
console.log('2');
}else if(document.selection){
console.log('3');
t = document.selection.createRange().text;
}
return t;
}
$('.text_speech').live('click',function(e){
e.preventDefault();
var textTest='';
textTest=getSelectedTexts();
console.log(textTest);
})
My console returns
1
>Selection <------object
anchorNode: Text
anchorOffset: 2
baseNode: Text
baseOffset: 2
extentNode: Text
extentOffset: 1
focusNode: Text
focusOffset: 1
isCollapsed: false
rangeCount: 1
type: "Range"
__proto__: Selection
I am not sure how to get the selected texts. Anyone can help me about it? Thanks a lot!
window.getSelection() returns a Selection object.
If you want to get the selected text without thinking more, you should do:
window.getSelection().toString()
But if your selection can be more complex, you should read the MDN documentation.
For example the user might select text with multiple tags:
<div class="text_speech">
<p>First paragraph</p>
<p>Second paragraph <strong>big</strong></p>
</div>
If the user select all the sentence 'paragraph Second paragraph big' then the easy way won't work.
Try:
console.log(textTest.toString());