jQuery appended text is invisible - javascript

I'm trying to create a couple of buttons above a textarea to insert some HTML code -- a VERY poor-man's HTML editor. I have a couple of INPUT elements, and I'm using jQuery to set a click handler that will call's jQuery's append() or html() or text() functions.
The handler fires, it shows a debug alert(), but the text I'm trying to append doesn't show up in the textarea. When I inspect the textarea in Firebug, I see the text I'm appending as a child of the textarea -- but it's dimmed, as when an element's style is set to display:none. But Firebug's CSS inspector doesn't show any change to the display or visibility properties.
When I set the click handler to 'append()', and then click multiple times, in Firebug I see the text being added over and over again -- but each new chunk is still invisible. If I choose 'Edit HTML' in Firebug and then type some chars next to the appended text, the entire text block -- the text added by jQuery and the stuff I added in Firebug -- suddenly appear.
This also happens if I don't use a click handler, but call my append function using an inline handler like onclick="javascript:insert('bold');"
Anyone have any idea why the appended text is not displayed?
Here's the relevant code:
The HTML:
<input type='button' id='bold' value='B' onclick='javascript:insert("bold")' />
<textarea name='PersonalGreeting' id='PersonalGreeting'>default text</textarea>
The Javascript:
function insert( cmd ) {
switch ( cmd ) {
case 'bold':
$('#PersonalGreeting').append('<b>bold text here</b>');
break;
}
}

I would guess that jQuery is trying to append HTML DOM elements to the textarea.
Try using the val method to get and set the textarea's value, like this:
$('#PersonalGreeting').val($('#PersonalGreeting').val() + '<b>bold text here</b>');

The basic problem is that you can't put HTML inside a <textarea>. In fact, you can't append HTML elements to one at all. You could use the .val() method to change the text shown inside, but that won't make it bold. That will just make it have <b> showing as part of the text.
An off-the-shelf WYSIWYG editor like TinyMCE is free and easy to implement. Rather than reinvent the wheel (which is a lot harder than it might look), try an existing wheel out.

SLaks and VoteyDisciple are correct. You're usage of append is faulty as you are perceiving it as a string function.
From http://docs.jquery.com/Manipulation/append
Append content to the inside of every
matched element. This operation is the
best way to insert elements inside, at
the end, of all matched elements. It
is similar to doing an appendChild to
all the specified elements, adding
them into the document.
Reinventing the wheel on this one is likely more headache than its worth unless this is an attempt to create a superior, competing product or for your own experimentation.
Also, I would shy away from use of obtrusive JavaScript as you have shown in your example with onclick='javascript:insert("bold")' embedded in the input element. Instead, you'll have a more elegant solution with something like the following:
HTML
<input type="button" value="B" class="editor-command" >
<input type="button" value="I" class="editor-command" >
<input type="button" value="U" class="editor-command" >
JavaScript (not tested)
$(document).ready(function() {
var textarea = $('#PersonalGreeting')
$(".editor-command").each(function(i, node) {
textarea.val(textarea.val() + '<$>text here</$>'.replace(/\$/g, node.value);
});
});

If the main issue is the textarea not being visible, I would try this:
$('#PersonalGreeting').append('<b>bold text here</b>').show();
Might be worth a shot.
edit: In the vain of not trying to reinvent the wheel, I've had success with WYMEditor

You could do this:
$('#PersonalGreeting').append('[b]bold text here[/b]');
But that won't actually render the text as bold. To be honest I'm not actually sure how to render text as bold inside a textarea, I imainge some js trickery.

Related

How to send text within a textarea having the attribute style="display: none;" through C# and Selenium

I need to write in the textarea with the simple sendkeys function in selenium.
This textarea (ID = 'txtSkillsTaught-Value') is followed by a script tag where the visibility of the textarea is hidden i guess due to which I am not able to write text.
tried the simple
driver.findelment(By.Id("txtSkillsTaught-Value")).sendkeys("text");
even tried switching to the iframe above but didnt worked
attached the image of HTML code
thanks,
Amey
In one hand if it is not visible maybe is not a good idea put text inside... But, in the other hand I need made things like that sometimes. I usually change the visibility for this element before send the keys with a javascript execution in my selenium code (I use java but for C# should be more or less the same):
executeScript("$('.yui-button.yui-link-button').find(\"textarea[id='txtSkillsTaught-Value']\").css({'opacity':'1', 'visibility':'visible', 'display':'block', 'position':'relative', 'transform':'none'})");
driver.findelment(By.Id("txtSkillsTaught-Value")).sendkeys("text");
That should work.
As per the HTML you have provided the <textarea> is out of the <iframe> but having the attribute style="display: none;". So to send a character sequence to the <textarea> you can use the following solution:
IWebElement element = new WebDriverWait(driver, TimeSpan.FromSeconds(10)).Until(ExpectedConditions.ElementToBeClickable(By.XPath("//td[#class='t-editable-area']//textarea[#class='t-content t-raw-content' and #id='txtSkillsTaught-value']")));
((IJavaScriptExecutor)driver).ExecuteScript("return arguments[0].removeAttribute('style')", element);
element.SendKeys("Amey");

JavaScript DIV Editing Destroys Functionality of Other Elements

So my website is built using a company's software called Inksoft which leaves me very little to work in the way of customization. So I have to do many workarounds.
Here is my site's homepage.
The header on top of the page only has two links right now. "Products" and "Design Studio". My goal is to add an "About Us" link and "Buyers Guide" to the header as well.
I cannot add new content to the header using Inksoft's backend. So I coded a workaround to replace the content of existing DIV's within the header to say and link to where I want them to go.
The only issue is, the responsive mobile-nav loses functionality when this is implemented. As seen here on this test page.
The test page has the About Us in the top header, added by the use of this code:
<script>
$("#header-nav-designs").html('<document.write="<li id="header-nav-studio"><font color="#000000">About Us</font></li>');
</script>
So, the simplified question is: how do I implement this code without losing the responsive functionality of the nav bar?
The jQuery .html function will replace the HTML inside the target element. If you want to just append the one value, you likely want to .append to the element.
In addition, you aren't setting the HTML to a valid html string. You probably just want to get rid of the <document.write=" at the beginning of the string. The rest of it looks fine with just a cursory glance.
So:
<script>
$("#header-nav-designs").append('<li id="header-nav-studio"><font color="#000000">About Us</font></li>');
</script>
Edit:
After looking at it a little more, it appears as though the $('#header-nav-designs') that you are selecting is already an <li> which means you need to either select the parent <ul> list or you can use the jquery .after function instead.
<script>
$("#header-nav-designs").after('<li id="header-nav-studio"><font color="#000000">About Us</font></li>');
</script>
And as someone else commented above, you are getting an error on the page. It appears as though you are trying to get an element with the id divID and the appending some html to it, but there is no element with the id divID and so you are getting an error saying that you can't read the property innerHTML of null as the call to document.getElementById is returning null (element not found).
Element id header-nav-designs witch your code is referring have CSS style on line 170:
#header-nav-designs {display:none;}
The element will be hidden, and the page will be displayed as if the element is not there. With display:none;
If I understand you correctly your code selector points to wrong element id. It should point $(".header-nav > ul"). Document.write is not needed inside jQuery you need to give only an valid html string as argument.
jQuery html function erase html that is all ready inside element and replace it with html string given as argument. You have to use append if you want to add more html but not remove what is allready in element.
$(".header-nav > ul").append('<li><font color="#000000">About Us</font></li>');
$(".header-nav > ul").append('<li><font color="#000000">Buyers Guide</font></li>');

Adding style to untagged text

I have html that I can't change (as its coming from a clients database)
something like below. as you can see it is not wrapped in a tag, and I can't select the div, as I only want to target stuff under the sub_header (if it's present) with white-space:pre-line;
<span class="sub_header">Example:</span>
<br/>
Some text
That I need to wrap with white-space:pre-line;
As it displays on one line in html
all the way done to the div
</div>
Is this even possible?
$('div.container').css('white-space', 'pre-line');
$('div.container span.sub_header').css('white-space', 'normal');
That code should apply the CSS to the parent div (which I assumed has a class of container but change it to whatever) but not the child span. There are more elegant ways to do it (get inner content, exclude the span, then wrap it in another div styled as you need) but this will do in a jiffy assuming they are all in this format.
looking for something like this? http://jsfiddle.net/pKyG7/4/
<div>
<span class="sub_header">Example:</span><br/>
<div style="font:arial; font-size:26px; white-space:pre-line;">Some text
That I need to wrap with white-space:pre-line;
As it displays on one line in html
all the way done to the div.</div>
</div>
Here's a jQuery solution: http://jsfiddle.net/9PzeS/
$($('.sub_header + br')[0].nextSibling).wrap('<span style="white-space:pre-line"></span>');
Not sure if it's ideal and needs some error handling, but it seems to work.
Edit - Slightly more readable version:
var textNode = $('.sub_header + br')[0].nextSibling;
$(textNode).wrap('<span style="white-space:pre-line"></span>');
$('.sub_header + br')[0] selects the br tag in your example and gets the dom node. nextSibling is a vanilla js property that selects the next sibling, including text nodes.
I then wrap that with a span with the correct style.

Let a user copy the source code... of an element with jquery

I can understand basic javascript and jquery but I'm having a hard time understanding how to allow a user to see the source code of an element for example.
If I have an element on a webpage like this
`<p>Hi I'm an element</p>`
every body knows it will be displayed as this
Hi I'm an element
but I want a user to see this in its source code form
`<p>Hi I'm an element</p>`
How on earth is this done??
The basic idea is to get the HTML of an element, then show it somewhere as plain-text. We can use .html() to get the HTML and then .text() to output the same HTML as plain-text:
//on the click of a link
​$('a')​.on('click', function () {
//append a container with the plain-text HTML of an element
$('body').append($('<div />').text($('form').html()));
});​​
Here is the demo: http://jsfiddle.net/YbJfs/
Note that this does not get the actual <form> tag, but you could place the form in a container, select the container, and then use the .html() if that container and you'll have the <form> tag as well.
Also, if you want to add the HTML to a form input or text-area, you can use .val() rather than .text().
Here is a demo: http://jsfiddle.net/YbJfs/1/
You can use...
element.outerHTML;
...though it isn't technically the "source code". It's the HTML rendered by the browser, which may have some differences.
Also, you need a shim for Firefox 10 and lower.
function outerHTML(el) {
return el.outerHMTL || document.createElement('div')
.appendChild(el.cloneNode(true))
.parentNode
.innerHTML;
}
to grab the html of an element either use native javascripts innerHTML, or if you want to use jQuery use html() method. Examples ...
javascript:
var html = document.getElementById('myOb').innerHTML;
jQuery:
var html = $('#myOb').html();

How do I get just the visible text with jQuery (or Javascript)?

I have website that converts Japanese Kanji into Romaji (roman letters):
and the output shows and hides with CSS what the user needs to see depending on their input criteria. For example:
<div id="output"><span class="roman">watashi</span> <span class="english">I</span></div>
The interface allows the user to flip between and output of watashi or I depending on what they want to see. The CSS hides one or the other using jQuery and a toggle button. (the hiding mechanism involves simple adding a class to the body and letting CSS do its thing).
The problem is that when users copy/paste the text into Word it copies everything. So I decided to use a system to copy paste the text using JavaScript and jQuery, but the problem repeats itself:
$('#output').text() outputs watashi I even if I is invisible on the page itself rather than watashi. Is there any way to get just the visible text?
the other solutions did not give me what I needed.
Short Answer
my answer is :
$('#output *:not(:has(*)):visible').text()
plunkr
TL;DR
The problem with marcgg's solution
You should not ask the text of all element under some root element..
why? - it will repeat output and ignore hidden flag
lets look at a simple example
<div id="output" class="my-root">
<div class="some-div">
<span class="first" style="display:none"> hidden text </span>
<span class="second" > visible text </span>
</div>
<div>
now if I do $('#output').children(":visible").text()
I will get .some-div and .second..
when in fact .some-div is of no concern to me..
when I ask for text() on those elements, .some-div will return the hidden text as well..
so technically marcgg's solution is wrong IMHO...
The reason for my answer
Now, in order to properly answer the question, we have to make an assumption. One that, for me, seems reasonable enough.
The assumption is that text only appears in leaf elements..
So we won't see something like this:
<div id="output" class="my-root">
<div class="some-div">
<span class="first" style="display:none"> hidden text </span>
<span class="second" > visible text </span>
</div>
some text here..
<div>
Why does this assumption seem reasonable to me? two reasons:
Because it is hard to maintain a page that is constructed this way - and with time people with experience learn that and avoid it.
It is easy to convert your html to such a structure. just wrap parents' text with spans. So even if this assumption does not exist right now, it is easy to get there.
With that assumption, what you want to do is request all leaf elements (elements without children) , filter out the visible, and ask for their text..
$('#output *:not(:has(*)):visible').text()
This should generate the correct result.
Gotta have text outside leaf element?
the comments suggest sometimes you just got to have text outside leaf element
<div> This is some <strong style="display:none"> text </strong> </div>
As you can see, you have <strong> as a leaf and it is common to have text outside it like in this example.
You could go around it with the workaround I suggest above.. but what if you can't?
You can clone the dom and then remove all hidden elements.
The problem here is that in order for :visible selector or :hidden selectors to work, I must have the dom element on the document (which means actually visible to the user).
And so, this method comes with some side effects, so be careful.
Here is an example
for this html
<div id="output" class="my-root">
<span>
some text <strong style="display:none">here.. </strong>
</span>
</div>
This javascript works
$(function(){
var outputClone = $('#output').clone();
$('#output :hidden').remove();
console.log($('#output').text()); // only visible text
$('#output').replaceWith(outputClone);
console.log($('#output').text()); // show original state achieved.
})
see plunker here
as mentioned - side effects may appear like a momentary flicker, or some initialization script that should run.. some may be avoided with some original thinking (div with size 1px/1px to contain the clone alongside original content?) depending on your scenario.
Use the :visible selector of jQuery
In your case I think you want to do:
$('#output').children(":visible").text()
Try this in modern browsers (here 'element' is a non-JQuery DOM object):
function getVisibleText(element) {
window.getSelection().removeAllRanges();
let range = document.createRange();
range.selectNode(element);
window.getSelection().addRange(range);
let visibleText = window.getSelection().toString().trim();
window.getSelection().removeAllRanges();
return visibleText;
}
then:
getVisibleText(document.getElementById('output'));
Guy has the correct answer.
However, I was dealing with a "this" object, so to get his answer to work you need to use the following syntax...
$('*:not(:has(*)):visible', this).text()
var lookup = function(element, text) {
//DFS Recursive way of finding text on each level
//Visible only works on elements that take up space(i.e. not fixed position elements)
var results = element.children(':visible');
//Look at the text at each level with the children removed
var newText = '';
results.each(function(index, value) {
newText += $(value).clone()
.children()
.remove()
.end()
.text();
});
var moreResultText = '';
results.each(function(index, value) {
moreResultText += lookup($(value), text);
})
if (results.length > 0) {
return text + newText + moreResultText;
} else {
return text;
}
};
lookup($('#output'), ''));
Most of the other functions fall apart when run on large sections of a page, this should be a more accurate way to determine what is actually displayed to the user, without corrupting the page, and without returning text that is not visible to the user.
Be careful of course, this does not preserve any sense of formatting, and the spacing of the output may not be correct between elements. Also, it probably does not correctly order the returned text, in these aspects its usages will be limited. Another consideration is the real definition of visible is a little hard to nail down, but for this example I accept that ":visible" works for most common cases.
I use it to check if a page contains visible text(just run it on the body element), but it would probably work for this example too.
Instead of hiding a span, remove the span element and keep a reference to it. When the user clicks on the toggle button, remove the other one and insert the one you kept a reference to. The user won't be able to select something that isn't in the DOM anymore.

Categories