I'm getting an undefined is not a function error when I try and run the following code:
$(document).ready(function() {
$("#textarea").select(function() {
var selection = window.getSelection();
$("#upper").click(function() {
// alert(selection);
var upper = selection.toUpperCase();
var text = $("#textarea").text();
$("#textarea").html(text.replace(selection, upper));
});
});
});
I'm trying to select text from a textarea, and click a button to make the selection uppercase. Here is a JSFiddle of the complete code.
getSelection returns an object. You need to call toString() on it.
$(document).ready(function () {
var selection;
$("#textarea").select(function () {
selection = window.getSelection().toString();
});
$("#upper").click(function () {
if (selection) {
var upper = selection.toUpperCase();
var text = $("#textarea").text();
$("#textarea").html(text.replace(selection, upper));
}
});
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/8syb2d8j/4/
Notes:
your event handlers were nested (usually a bad idea)
I added a check to ensure that there is a selection before trying to upppercase it.
as, potentially, the text you highlight may occur more than one, using replace is actually not a great solution. Try highlight the second i and see what I mean. You should use the properties of the selection object instead to work out the exact part of the string that was selected,
After browsing around for portable solutions, I found the jQuery TextRange plugin, which, based on the demo is more than enough for this problem.
Related
I created a function that wraps the selected text in the h1 tag, and also deletes the tags when it is called again. The function also deletes tags and creates again if you select an area larger than the previous selection.
However, if after creating the heading from the selection, select another text, and then select the word again, then the tags do not include in the selection (although the selection completely coincides with the created heading). I use the Selection and Range methods.
It turns out that with repeated selection, the tags do not fall into Selection.
I also tried to do auto-replace using a regular expression, but this method is not good, because a phrase or a word can occur several times in the text.
Please tell me, is there any way in Javascript that allows us to find a node that matches this selection?
Maybe I didn’t explain well, and to better show, I created a snippet that demonstrates that. what's happening:
https://jsfiddle.net/narzantaria/2ex6bnq3/2/
Also here is the same code. The function is called by clicking the button:
document.getElementById('h1-trigger').addEventListener('click', function () {
headerFormatter();
});
function headerFormatter() {
let newTag = document.createElement('h1');
if (window.getSelection) {
let sel = window.getSelection();
if (sel.rangeCount) {
let range = sel.getRangeAt(0).cloneRange();
// create an object from range for querying tags
let rangeProxy = sel.getRangeAt(0).cloneContents();
if (rangeProxy.querySelector('h1')) {
let tagContent = rangeProxy.querySelector('h1').innerHTML;
// compare selection length with queried tag length
if (range.startOffset == 1) {
tagContent = tagContent.replace(/(<([^>]+)>)/ig, "");
range.deleteContents();
range.insertNode(document.createTextNode(tagContent));
sel.removeAllRanges();
sel.addRange(range);
return;
}
else {
let rangeToString = range.toString().replace(/(<([^>]+)>)/ig, "");
range.deleteContents();
range.insertNode(document.createTextNode(rangeToString));
sel.removeAllRanges();
sel.addRange(range);
return;
}
} else {
range.surroundContents(newTag);
sel.removeAllRanges();
sel.addRange(range);
}
}
}
}
Regards.
You'll have to do a few things to achieve the type of functionality you desire:
When you insert an H1 tag, add an identifier to it. Store this identifier along with the selected range values. Also, use a boolean flag variable to show the text is being highlighted.
Next time when you click the button, check whether the flag variable is true. If it is, using the stored range values from the previous selection, remove the h1 highlighting.
Once done, you can now add the highlighting to the new range and repeat step 1.
This might be a lengthier approach, but it may be more effective than your current approach.
I hope this helps. Thanks!
A flowchart to explain the steps above
You can get tag where the selection starts/ends using getSelection().anchorNode and getSelection().focusNode.
I am writing a test with selenium using JavaScript.
At one point in my test, I iterate through an array of input elements and want to fill in a value for the inputs which are visible.
if (textInputs.length > 0) {
console.log('handling text input');
var i, textInputLen;
for (i = 0, textInputLen = textInputs.length; i < textInputLen; i++) {
(function (index) {
if (textInputs[index].isDisplayed()) {
textInputs[index].sendKeys("custom text box - " + textBoxes);
textBoxes++;
}
}(i))
}
}
I keep getting the following error ElementNotVisibleError: element not visible because it is trying to fill in an input which is not displayed on the DOM. Why is it not working? How can I get it working?
I have tried a few thing instead of isDisplayed():
I executed JS to see if the element had visibility set to hidden or display to none. Issue here is that the element bing displayed, in this case, is due to a slew of things and I am looking for something more general. IN this case, isDisplayed() would be exactly what I want.
Please advise
The method .isDisplayed() returns a promise and not the displayed state. Use then to get the resolved result:
element.isDisplayed().then(function(state){
console.log(state):
});
But a better way would be to filter the list of elements with webdriver.promise.filter:
var textInputs = driver.findElements(By.css("input[type=text]"));
webdriver.promise.filter(textInputs, function(element) {
return element.isDisplayed();
}).then(function(element) {
element.sendKeys("my text");
});
Got a question for you javascript gurus out there. I'm creating a sidebar tool that is comprised of a few different text input fields. The purpose of this tool is to edit copy on a template. I've tried to pass the data entered into the field onchange, but I'm running into problems dumping the data into my js object. This is somewhat what I have in mind:
$('myInputField').(function(){
$(this).onChange(){
// write to variable X
}
});
Essentially I want to have what I'm typing in the input be mimicked live and then I can parse the changes to my database.
$('#myInputField').(function(){
$(this).onkeyup(){
x = this.value;
}
});
or more succinctly:
$('#myInputField').onkeyup(){
x = this.value;
});
You're just looking for the value that's in myInputField within that event handler? Something like this?:
$('myInputField').(function(){
$(this).onChange(){
x = $(this).val();
}
});
I don't remember off the top of my head if this is already a jQuery object. If it is, then this should work and perhaps skip a little bit of overhead:
x = this.val();
Additionally, you can explicitly reference the field with a normal jQuery selector if this is ever overridden with a different context, or if you want to reference other fields as well, etc.:
x = $('myInputField').val();
The problem is, on IE, the onchange event doesn't work on INPUT elements. Thus, you have to use the onkeypress or the onblur event depending on what you want to do.
JS way:
document.getElementById('myInputField').onblur = function() {
var x = this.value
}
jQuery way:
$('#myInputField').blur(function() {
var x = this.value
})
Wouldn't a simply keyup event on the input fields be sufficient?
jQuery:
$('textarea').keyup(function() {
$('#foo').html($(this).val());
});
HTML:
<textarea></textarea>
<div id="foo"></div>
jsFiddle example.
Considering features like EditArea's and CodeMirror's autocomplete, I was wondering if, like Dreamweaver, there is a way to detect if the last word you entered is in a certain list then provide the same kind of suggestion box but with the function's arguments. I imagine you would use a regular expression on the entire field or possibly split() the whole thing (or the current line) then use the length attribute of the array to find the last bit of text, then do something involving an indexOf-like operation; however, this seems like it would get a bit resource-intensive. It almost looks like I've answered my own question, but it always helps to fully explain one's quandary, especially publicly. There's gotta be a better solution than mine. I appreciate any input. Thank you.
Put the list of words to match in an object, have the text or options to display as the value. Then on keyup or keypress you can get the last word of the text area using a function like:
function showLastWord(id){
var el = document.getElementById(id);
var lastWord = el.value.match(/\w+$/);
return lastWord? lastWord[0] : '';
}
Then check if the word is in the list and do stuff appropriately.
Edit
A small example is:
<textarea onkeyup="showHelp(this);"></textarea>
<script>
var getLastWord = (function() {
re = /\w+$/;
return function (s){
var lastWord = s.match(re);
return lastWord? lastWord[0] : '';
}
}());
var keyWords = {foo:'foo was typed',bar:'bar was typed'};
function showHelp(el) {
var lastWord = getLastWord(el.value);
// Check for matching own property of keyWords
if (keyWords.hasOwnProperty(lastWord)) {
// Do stuff
console.log(keyWords[lastWord]);
}
}
I am trying to create a function that given a divid, and a list of classes, will then do some text replacing inside them.
Having learned of how Firefox Dom is handling text nodes differently, I read that I had to use javascript to loop through the elements, sibling to nextSibling.
The last obstacle I had in my script, of which you see a small portion of, is getting the classname. I need the class name so that I can filter down what content get's text replaced.
Having looked all the answers, and with the help of a co-worker named Ryan at work, we have redone this in jquery.
$(divid).find(".status_bar").each( function() {
var value = $.trim($(this).text());
// if value is not defined thru browser bugs do not replace
if (typeof(value) != 'undefined') {
// it is a text node. do magic.
for (var x = en_count; x > 0; x--) {
// get current english phrase
var from = en_lang[x];
// get current other language phrase
var to = other_lang[x];
if (value == from) {
console.log('Current Value ['+value+'] English ['+from+'] Translation ['+to+']');
value = to;
$(this).attr('value', to);
}
}
}
});
This currently works in all areas, except in the replacing of text.
The reason I had originally with doing this in jQuery, had to be not sure I could loop thru elements, and avoid the problem with firefox and text nodes.
I am doing a loop of all elements inside a div, and I now need to get the classname of the element that I am looping by.
Then i can check if the current element's class is one, I need to do something with...
// var children = parent.childNodes, child;
var parentNode = divid;
// start loop thru child nodes
for(var node=parentNode.firstChild;node!=null;node=node.nextSibling){
var myclass = (node.className ? node.className.baseVal : node.getAttribute('class'));
}
But this code for getting the classname only get's null values.
Any suggestions?
For those of you who are trying to figure out what the whole point is, read this JavaScript NextSibling Firefox Bug Fix I have code that does my language translation that works in Google Chrome and IE. But when I use it in Firefox, and try to translate div content after ajax has loaded it, it fails because of the whitespace issue.
I really don't have a preference of jQuery or Pure Javascript, I just want a working solution.
Thank you all for being patient. I personally thought I was extremely clear in my description, I apologize if it wasn't. I wasn't trying to be obscure or make it difficult to get help. But please don't insult me, by implying I am trying to make it unclear.
Thanks.
Hm... You have jQuery but don't use it?
$(divid).children(".yourSpecialClassName").each( function() {
doSomethingWith(this);
});
To get the CSS class attribute value, this will do:
$(divid).children().each( function() {
alert(this.className);
});
Based on the function you posted now, you want this:
$(divid).find(".status_bar").each( function() {
$(this).text( function(i, text) {
var x = $.inArray(en_lang, $.trim(text));
if (x > -1) {
console.log('Current Value ['+text+'] English ['+en_lang[x]+'] Translation ['+other_lang[x]+']');
return other_lang[x];
}
return text;
});
});
And please, don't ever use "do magic" as a comment again. This is incredibly lame.
EDIT. This can be made much more efficient (superfluous console.log() removed):
$(divid).find(".status_bar").each( function() {
// prepare dictionary en_lang => other_lang
var dict = {};
$.each(en_lang, function(x, word) { dict[word] = other_lang[x]; });
$(this).text( function(i, text) {
var t = $.trim(text);
return (t in dict) ? dict[t] : text;
});
});
if you are using jquery you can do this:
$("#myDiv").find("*").each(
function(){
var myclass = $(this).attr("class");
}
);
Your sample code doesn't make sense.
$(this).attr('value', to);
'value' is an attribute of the tag, not the text content.
Did you really mean to do this instead?
$(this).text(to);
Also, you've re-edited your question, but you're still trying to loop through the child nodes using non-jQuery methods. You said "The last obstacle I had in my script, of which you see a small portion of, is getting the classname. I need the class name so that I can filter down what content get's text replaced."
If you are using jQuery it is completely unnecessary to loop through anything to get a class name. You simply have to use a proper selector in the first place.
$(divid).find(".status_bar.replaceme").each( function() {
// .replaceme is whatever class you're using for the stuff you want to change
// .status_bar.replaceme matches all elements with BOTH status_bar and replaceme classes
var value = $.trim($(this).text());
// if value is not defined thru browser bugs do not replace
if (typeof(value) != 'undefined') {
// it is a text node. do magic.
// NOTE: The following is inefficient but I won't fix it.
// You're better off using an associative array
for (var x = en_count; x > 0; x--) {
// get current english phrase
var from = en_lang[x];
// get current other language phrase
var to = other_lang[x];
if (value == from) {
console.log('Current Value ['+value+'] English ['+from+'] Translation ['+to+']');
// value = to; <-- useless, get rid of it.
$(this).text(to);
// or $(this).html(to);
}
}
}
});