Changing <p> text on the fly

M

Margaret MacDonald

I'm a js novice trying to teach myself. I'm using Flanagan's
'Javascript, the definitive guide' from O'Reilly as a text. But
either I'm dopier than usual or its layout doesn't match my learning
style very well, because I seem to be having a dreadful time getting
to grips with even the simplest things.

Currently, I'm trying to change the text that follows a <p> element in
a little test program. My test program has a table with two <td>
elements, each having a <p> and a <textarea>, and a function swap()
that looks at the .value of the <textarea> and, if it's not zero,
copies the value to the other <textarea> and zeroes the current one
out. That part works fine--I can click the button and move the text
back and forth as much as I like.

Trying to do the same thing with the <p> text works up through the
part where I assign the new text to the .data property. But the
display doesn't then update, it just sits there. I can watch the text
move back and forth between the two <textareas>, but there's no
corresponding change at all in the two <p> elements.

Am I not understanding some key piece? Could my browser (Opera7) be
broken?

Any insights greatly appreciated!

Margaret

(here's half the function)

var s,p ;
if ( document.test.leftta.value > "")
{
// copy it to the right side
s = String(document.test.leftta.value) ;
document.test.rightta.value = s.toLowerCase() ;
p = document.getElementById("rightp") ;
if ( p )
{
p.data = s.toString() ;
document.test.rightta.value += "\n"+p.data ;
}
else { document.test.rightta.value += "\nbugger!" ; }

// clear out the left side
document.test.leftta.value = "" ;
p = document.getElementById("leftp") ;
p.data = "" ;
}
 
R

RobG

Margaret MacDonald wrote:
[snip]
Currently, I'm trying to change the text that follows a <p> element in
a little test program. My test program has a table with two <td>

You identify your error here - the text *follows* the <p>, it has its
own node (if use a DOM inspector, you'll see it). So what you need is
to change the value of the text node after the <p>.

A good place to start is to do an alert with the thing you are trying
to access, e.g. alert(document.test.rightta.value) to see what it is
at the moment, before you modify it.
s = String(document.test.leftta.value) ;

I don't think there is any need to use String(), JavaScript types
everything as string by default - though you can make it do otherwise.
p.data = s.toString() ;

Now if you'd done "alert(p.data)" you would have gotten "undefined",
document.test.rightta.value += "\n"+p.data ;

Below is some code that works, it should help. Try using a browser
with a DOM inspector (almost any browser other than IE) so you can see
what's happening in your document.

Note that in the <p> that I am modifying, I have put in "&nbsp;". This
is to create a blank text node, otherwise in the DOM method I'd have to
test to see if my <p> had a text node and if not, create and add one,
then change it's data attribute.

The second method shows modifying the <p> content using innerHTML,
which is often much simpler but doesn't work on all elements. All the
above shows why it is simpler to use innerHTML rather than DOM methods
to change stuff quite often.

Have fun - Rob.

<html>
<head>
<title>Change Text</title>
<script type="text/javascript">
function changeTxt(t,p) {
if (document.getElementById) {
document.getElementById(p).firstChild.data = t;
}
}
function changeTxt2(t,p) {
if (document.getElementById) {
document.getElementById(p).innerHTML = t;
}
}
</script>
</head>
<body>
<form action="">
<textarea name="aTa" cols="30" rows="3"></textarea><br>
<input type="button" value="Change p text using DOM"
onclick="changeTxt(this.form.aTa.value,'aPara');">
<input type="button" value="Change p text using innerHTML"
onclick="changeTxt2(this.form.aTa.value,'aPara');">
</form>
<p>Click a button, whatever is in the text area will appear
below here:</p>
<p id="aPara" style="background-color: #eeb;">&nbsp;</p>
</body>
</html>
 
D

DU

RobG said:
Margaret MacDonald wrote:
[snip]
Currently, I'm trying to change the text that follows a <p> element in
a little test program. My test program has a table with two <td>


You identify your error here - the text *follows* the <p>, it has its
own node (if use a DOM inspector, you'll see it). So what you need is
to change the value of the text node after the <p>.

A good place to start is to do an alert with the thing you are trying
to access, e.g. alert(document.test.rightta.value) to see what it is
at the moment, before you modify it.
s = String(document.test.leftta.value) ;


I don't think there is any need to use String(), JavaScript types
everything as string by default - though you can make it do otherwise.
p.data = s.toString() ;


Now if you'd done "alert(p.data)" you would have gotten "undefined",
document.test.rightta.value += "\n"+p.data ;


Below is some code that works, it should help. Try using a browser
with a DOM inspector (almost any browser other than IE) so you can see
what's happening in your document.

Note that in the <p> that I am modifying, I have put in "&nbsp;". This
is to create a blank text node, otherwise in the DOM method I'd have to
test to see if my <p> had a text node and if not, create and add one,
then change it's data attribute.

The second method shows modifying the <p> content using innerHTML,
which is often much simpler but doesn't work on all elements. All the
above shows why it is simpler to use innerHTML rather than DOM methods
to change stuff quite often.

Have fun - Rob.

<html>
<head>
<title>Change Text</title>
<script type="text/javascript">
function changeTxt(t,p) {
if (document.getElementById) {
document.getElementById(p).firstChild.data = t;

I have been scolded by regulars in this newsgroup before on this very
same question for not properly detecting the type of node before making
such change.

if (document.getElementById && document.getElementById(p).firstChild &&
document.getElementById(p).firstChild.nodeType == "3") {
document.getElementById(p).firstChild.data = t;

}
}
function changeTxt2(t,p) {
if (document.getElementById) {
document.getElementById(p).innerHTML = t;

This is a bit counter-productive. Any/every browser which supports
getElementById also supports innerHTML. Because innerHTML was introduced
before DOM 2 getElementById. So, here you can condense both functions
into a single one.

Also, innerHTML "costs" more, is more cpu and RAM demanding than
IE-specific innerText (which Opera 7 supports) and DOM3 textContent
attribute (DOM3 textContent has been supported by Mozilla since version
1.5; soon Opera 7 will also support DOM3 textContent). So you might want
to keep innerHTML as the last fallback resource.

DU
 
R

RobG

DU wrote:
[snip]
I have been scolded by regulars in this newsgroup before on this very
same question for not properly detecting the type of node before making
such change.

Point taken, however I noted that I wasn't detecting the node, or even
that it existed. The exercise was simply to show modifying the text of
a paragraph using two different methods.
if (document.getElementById && document.getElementById(p).firstChild &&
document.getElementById(p).firstChild.nodeType == "3") {
document.getElementById(p).firstChild.data = t;

I would suggest that getElementById is tested on its own, then the
childNodes collection interrogated to find out if any text nodes exist.

The program logic can then decide whether to add a text node (if either
none exist or the one(s) found are in the 'wrong' place) or
modify/replace an existing one.

The function below finds the first text node that is a child of the
given reference and replaces its content. If no child text node is
found, it adds one with the text from the textarea as content.

function changeTxt(t,p) {
if (document.getElementById) {
var foundTxtNode = false;
var daBits = document.getElementById(p).childNodes;
for (var i=0; i<daBits.length; i++) {
if (daBits.nodeName == '#text') {
foundTxtNode = true;
daBits.data = t;
break;
}
}
if (!foundTxtNode) {
var x = document.createTextNode(t);
document.getElementById(p).appendChild(x);
}
}
}

A by-product is that if an empty string is passed to the function, the
text node is deleted in Firefox but not in IE. In this case, innerHTML
is vastly simpler (only 3 lines of code) and more consistent - if an
empty string is passed, the text node is always deleted in both Firefox
and IE.

Another consequence of the above is that innerHTML will completely
replace the content of the <p>, thereby removing anything else that is
there whereas the DOM method will only change/add a text node, leaving
the rest of the content alone - though this can be done with innerHTML
too.

So horses for courses, as long as the OP works out what is reasonable
behaviour and what isn't for her circumstance.
This is a bit counter-productive. Any/every browser which supports
getElementById also supports innerHTML. Because innerHTML was introduced
before DOM 2 getElementById. So, here you can condense both functions
into a single one.

The OP said this was just an exercise to learn about manipulating
elements, my intention was just to show how simple innerHTML can be as
opposed to using DOM create/append methods. It is particularly useful
when many small elements need to be added and you want to avoid slabs
of create/append statements.

Purists tend to hate innerHTML because it does seem very much like a
hack (kinda like getting a program to do something by modifying the
underlying code rather than using a separate function I guess) but that
doesn't wash with me when it is so useful in certain circumstances.
Also, innerHTML "costs" more, is more cpu and RAM demanding than
IE-specific innerText (which Opera 7 supports) and DOM3 textContent
attribute (DOM3 textContent has been supported by Mozilla since version
1.5; soon Opera 7 will also support DOM3 textContent). So you might want
to keep innerHTML as the last fallback resource.

I understand your point on the CPU/RAM usage of innerHTML, however it
is insignificant for the minimal use advise above. I use a very old
computer for development and testing (it was low-end 5 years ago when I
bought it) and I have used vastly more complex JavaScript manipulating
big chunks of HTML and DOM elements with no noticeable performance
issues. And I would never expect a general web site to be dependent on
highly complex or CPU-intensive code of any kind, much less JavaScript
unless that was the specific domain of the site (say games or similar).

I only use innerHTML because it has reasonable cross-browser support.
I have no intention of promoting the use of innerText (or outerHTML for
that matter).

Cheers, Rob :)
 
M

Margaret MacDonald

Thanks, Rob and Doc. At least I wasn't totally off the mark, though
I'm sure I would have gone mad trying to make things work as per the
book. Who would have thought that the standard .data property would
be superceded by Microsoft's choice rather than supplemented by it?

Margaret
 
F

Fred Oz

Margaret MacDonald wrote:
[snip]
book. Who would have thought that the standard .data property would
be superceded by Microsoft's choice rather than supplemented by it?

Not quite sure what you mean here. This is nothing to do with
Microsoft, particularly as you aren't using IE. It is MS's
implementation JavaScript called JSscript that causes most issues.
Developers who use only IE (and therefore Windows) often end up with
IE-specific code because of MS's extensions that are not supported in
other environments.

The problem in your case was that you were trying to change the text of
a <p> without realising that the text is attached to a text node that
is a child of the <p> - as per the document object model (DOM). MS
implements the DOM reasonably well.

The second issue is that if there is no text node after the <p> you
have to either use a DOM method (createTextNode...appendChild... etc.)
or use innerHTML. Otherwise your browser likely will not display the
text.

I hope that clears things up - :) Fred
 
M

Margaret MacDonald

Additional thanks to Rob for urging me to use a DOM inspector, and to
Fred for intimating that I had a conceptual error. As I discovered
when I used the DOM inspector, my mental model was reasonable, but
completely wrong :-(

Unfortunately something's still wrong, but I don't know what. When I
look at a tree-looking structure and hear it described in terms of
'parent' and 'child', I naturally think of a linked list. But that
doesn't seem to be what's going on here, because when I try to move
(e.g.) the bold-italic-link text (below) from one <td> to another by
moving the children of the <td>

<td id="ltd">
This is <a href=""><u><b>test</b></u></a> text
</td>

only the 'This is' and 'text' move. The anchor and its embedded tags
and text don't. So is the <a> not a child of the <td>? The DOM
inspector shows it being, and common sense says it ought to be. But
if it is, why doesn't it move?

An additional goofy part is that, on the next click (moving things
back from right to left) only 'text' moves. The 'this is' doesn't.
Now on the left side I have the anchor and the 'text'. The third
click moves both the anchor and the 'text' from the left to the right,
so after 3 clicks I finish up with everything in the right <td> as I
expected after the first click. Another series of 3 clicks moves
everything back to the left.

I can't imagine what could be going on. Every explanation I can come
up with seems to have holes in it.

Any insights gratefully received!

Margaret

(I determine which is src and which dest by toggling a global boolean
on each call)

kid = src.childNodes ;
kids = kid.length ;

for ( n=0 ; n<kids ; n++ )
dest.appendChild( kid[n] ) ;
 
M

Margaret MacDonald

Just for completeness, I should probably mention that when I monitor
the move, I get a .length of 3 kids, but kids 0 and 1 are reported to
be type 3 (text) and kid 2 gives me an undefined error. But, once the
leading text node has been moved, the routine can see the type 1 <a>
node.

The same thing happens in both FF1PR and O7, so it's probably not a
bug, but I'll be hedgehogged if I can imagine what it is.

Margaret
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top