I have some JavaScript code that works in IE containing the following:
myElement.innerText = "foo";
However, it seems that the 'innerText' property does not work in Firefox. Is there some Firefox equivalent? Or is there a more generic, cross browser property that can be used?
Update: I wrote a blog post detailing all the differences much better.
Firefox uses W3C standard Node::textContent, but its behavior differs "slightly" from that of MSHTML's proprietary innerText (copied by Opera as well, some time ago, among dozens of other MSHTML features).
First of all, textContent whitespace representation is different from innerText one. Second, and more importantly, textContent includes all of SCRIPT tag contents, whereas innerText doesn't.
Just to make things more entertaining, Opera - besides implementing standard textContent - decided to also add MSHTML's innerText but changed it to act as textContent - i.e. including SCRIPT contents (in fact, textContent and innerText in Opera seem to produce identical results, probably being just aliased to each other).
textContent is part of Node interface, whereas innerText is part of HTMLElement. This, for example, means that you can "retrieve" textContent but not innerText from text nodes:
var el = document.createElement('p');
var textNode = document.createTextNode('x');
el.textContent; // ""
el.innerText; // ""
textNode.textContent; // "x"
textNode.innerText; // undefined
Finally, Safari 2.x also has buggy innerText implementation. In Safari, innerText functions properly only if an element is
neither hidden (via style.display == "none") nor orphaned from the document. Otherwise, innerText results in an empty string.
I was playing with textContent abstraction (to work around these deficiencies), but it turned out to be rather complex.
You best bet is to first define your exact requirements and follow from there. It is often possible to simply strip tags off of innerHTML of an element, rather than deal with all of the possible textContent/innerText deviations.
Another possibility, of course, is to walk the DOM tree and collect text nodes recursively.
Firefox uses the W3C-compliant textContent property.
I'd guess Safari and Opera also support this property.
If you only need to set text content and not retrieve, here's a trivial DOM version you can use on any browser; it doesn't require either the IE innerText extension or the DOM Level 3 Core textContent property.
function setTextContent(element, text) {
while (element.firstChild!==null)
element.removeChild(element.firstChild); // remove all existing content
element.appendChild(document.createTextNode(text));
}
jQuery provides a .text() method that can be used in any browser. For example:
$('#myElement').text("Foo");
As per Prakash K's answer Firefox does not support the innerText property. So you can simply test whether the user agent supports this property and proceed accordingly as below:
function changeText(elem, changeVal) {
if (typeof elem.textContent !== "undefined") {
elem.textContent = changeVal;
} else {
elem.innerText = changeVal;
}
}
A really simple line of Javascript can get the "non-taggy" text in all main browsers...
var myElement = document.getElementById('anyElementId');
var myText = (myElement.innerText || myElement.textContent);
Note that the Element::innerText property will not contain the text which has been hidden by CSS style "display:none" in Google Chrome (as well it will drop the content that has been masked by other CSS technics (including font-size:0, color:transparent, and a few other similar effects that cause the text not to be rendered in any visible way).
Other CSS properties are also considered :
First the "display:" style of inner elements is parsed to determine if it delimits a block content (such as "display:block" which is the default of HTML block elements in the browser's builtin stylesheet, and whose behavior as not been overriden by your own CSS style); if so a newline will be inserted in the value of the innerText property. This won't happen with the textContent property.
The CSS properties that generate inline contents will also be considered : for example the inline element <br \> that generates an inline newline will also generate an newline in the value of innerText.
The "display:inline" style causes no newline either in textContent or innerText.
The "display:table" style generates newlines around the table and between table rows, but"display:table-cell" will generate a tabulation character.
The "position:absolute" property (used with display:block or display:inline, it does not matter) will also cause a line break to be inserted.
Some browsers will also include a single space separation between spans
But Element::textContent will still contain ALL contents of inner text elements independantly of the applied CSS even if they are invisible. And no extra newlines or whitespaces will be generated in textContent, which just ignores all styles and the structure and inline/block or positioned types of inner elements.
A copy/paste operation using mouse selection will discard the hidden text in the plain-text format that is put in the clipboard, so it won't contain everything in the textContent, but only what is within innerText (after whitespace/newline generation as above).
Both properties are then supported in Google Chrome, but their content may then be different. Older browsers still included in innetText everything like what textContent now contains (but their behavior in relation with then generation of whitespaces/newlines was inconsistant).
jQuery will solve these inconsistencies between browsers using the ".text()" method added to the parsed elements it returns via a $() query. Internally, it solves the difficulties by looking into the HTML DOM, working only with the "node" level. So it will return something looking more like the standard textContent.
The caveat is that that this jQuery method will not insert any extra spaces or line breaks that may be visible on screen caused by subelements (like <br />) of the content.
If you design some scripts for accessibility and your stylesheet is parsed for non-aural rendering, such as plugins used to communicate with a Braille reader, this tool should use the textContent if it must include the specific punctuation signs that are added in spans styled with "display:none" and that are typically included in pages (for example for superscripts/subscripts), otherwise the innerText will be very confusive on the Braille reader.
Texts hidden by CSS tricks are now typically ignored by major search engines (that will also parse the CSS of your HTML pages, and will also ignore texts that are not in contrasting colors on the background) using an HTML/CSS parser and the DOM property "innerText" exactly like in modern visual browsers (at least this invisible content will not be indexed so hidden text cannot be used as a trick to force the inclusion of some keywords in the page to check its content) ; but this hidden text will be stil displayed in the result page (if the page was still qualified from the index to be included in results), using the "textContent" property instead of the full HTML to strip the extra styles and scripts.
IF you assign some plain-text in any one of these two properties, this will overwrite the inner markup and styles applied to it (only the assigned element will keep its type, attributes and styles), so both properties will then contain the same content. However, some browsers will now no longer honor the write to innerText, and will only let you overwrite the textContent property (you cannot insert HTML markup when writing to these properties, as HTML special characters will be properly encoded using numeric character references to appear literally, if you then read the innerHTML property after the assignment of innerText or textContent.
myElement.innerText = myElement.textContent = "foo";
Edit (thanks to Mark Amery for the comment below): Only do it this way if you know beyond a reasonable doubt that no code will be relying on checking the existence of these properties, like (for example) jQuery does. But if you are using jQuery, you would probably just use the "text" function and do $('#myElement').text('foo') as some other answers show.
innerText has been added to Firefox and should be available in the FF45 release: https://bugzilla.mozilla.org/show_bug.cgi?id=264412
A draft spec has been written and is expected to be incorporated into the HTML living standard in the future: http://rocallahan.github.io/innerText-spec/, https://github.com/whatwg/html/issues/465
Note that currently the Firefox, Chrome and IE implementations are all incompatible. Going forward, we can probably expect Firefox, Chrome and Edge to converge while old IE remains incompatible.
See also: https://github.com/whatwg/compat/issues/5
This has been my experience with innerText, textContent, innerHTML, and value:
// elem.innerText = changeVal; // works on ie but not on ff or ch
// elem.setAttribute("innerText", changeVal); // works on ie but not ff or ch
// elem.textContent = changeVal; // works on ie but not ff or ch
// elem.setAttribute("textContent", changeVal); // does not work on ie ff or ch
// elem.innerHTML = changeVal; // ie causes error - doesn't work in ff or ch
// elem.setAttribute("innerHTML", changeVal); //ie causes error doesn't work in ff or ch
elem.value = changeVal; // works in ie and ff -- see note 2 on ch
// elem.setAttribute("value", changeVal); // ie works; see note 1 on ff and note 2 on ch
ie = internet explorer, ff = firefox, ch = google chrome.
note 1: ff works until after value is deleted with backspace - see note by Ray Vega above.
note 2: works somewhat in chrome - after update it is unchanged then you click away and click back into the field and the value appears.
The best of the lot is elem.value = changeVal; which I did not comment out above.
As in 2016 from Firefox v45, innerText works on firefox, take a look at its support: http://caniuse.com/#search=innerText
If you want it to work on previous versions of Firefox, you can use textContent, which has better support on Firefox but worse on older IE versions: http://caniuse.com/#search=textContent
What about something like this?
//$elem is the jQuery object passed along.
var $currentText = $elem.context.firstChild.data.toUpperCase();
** I needed to make mine uppercase.
Just reposting from comments under the original post. innerHTML works in all browsers. Thanks stefita.
myElement.innerHTML = "foo";
found this here:
<!--[if lte IE 8]>
<script type="text/javascript">
if (Object.defineProperty && Object.getOwnPropertyDescriptor &&
!Object.getOwnPropertyDescriptor(Element.prototype, "textContent").get)
(function() {
var innerText = Object.getOwnPropertyDescriptor(Element.prototype, "innerText");
Object.defineProperty(Element.prototype, "textContent",
{ // It won't work if you just drop in innerText.get
// and innerText.set or the whole descriptor.
get : function() {
return innerText.get.call(this)
},
set : function(x) {
return innerText.set.call(this, x)
}
}
);
})();
</script>
<![endif]-->
It's also possible to emulate innerText behavior in other browsers:
if (((typeof window.HTMLElement) !== "undefined") && ((typeof HTMLElement.prototype.__defineGetter__) !== "undefined")) {
HTMLElement.prototype.__defineGetter__("innerText", function () {
if (this.textContent) {
return this.textContent;
} else {
var r = this.ownerDocument.createRange();
r.selectNodeContents(this);
return r.toString();
}
});
HTMLElement.prototype.__defineSetter__("innerText", function (str) {
if (this.textContent) {
this.textContent = str;
} else {
this.innerHTML = str.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/\n/g, "<br />\n");
}
});
}
Related
How can I ensure that this:
$('.graph svg').append('<polyline points="' + '3,' +point+ ' 97,' +point+ '"style="fill:none;stroke:#FFFFFF;stroke-width:0.5"/>');
Actually results in a self closing tag ..../>
Rather than closing with </polyline>
For some reason only the former renders on iOS.
It doesn't result in any tags at all; it results in elements in the DOM. Tags are textual means of describing elements. When you give that string to jQuery, it asks the browser to parse that string and create elements (objects) in memory. The only tags involved are the ones you give to jQuery.
From your update (comment):
...is there another way of doing this that avoids the append method? Here's a fiddle that refuses to work on iOS http://jsfiddle.net/rCfrF/23
That doesn't work for me on Chrome, Firefox, or IE either. I don't think you can add to SVG elements like that, I think jQuery tries to create an HTML element polyline rather than the SVG polyline (which is namespaced).
This works on Chrome, Firefox, IE, and my iPhone 5: Updated version of your fiddle on JSBin (jsFiddle doesn't work properly on my iPhone 5)
function clickappend() {
var svg = $("#graph svg")[0];
var polyline = svg.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'polyline');
polyline.setAttribute("points", "20,0 20,100");
polyline.style.fill = "none";
polyline.style.stroke = "#232323";
polyline.style.strokeWidth = "0.5";
svg.appendChild(polyline);
alert('ran');
}
You could use:
$('.graph svg').html($('.graph svg').html() + '<polyline points="' + '3,' +point+ ' 97,' +point+ '"style="fill:none;stroke:#FFFFFF;stroke-width:0.5"/>');
This is the inspector's problem. There is a substantial difference between void elements (aka self-closing elements) and others, in that they cannot accept descendant nodes. polyline is such a void element. The inspector may show it as having a closing tag, but it shouldn't be able to accept methods such as – if you tried to insert content between its opening and closing tags that content would likely be inserted after it in the DOM.
I have a piece of Javascript code that locates the single table in the DOM then tries to manipulate its first child, the thead (actually, it iterates though the children of that child, the tr entries but that's not important to the question). The code to do this is:
var tableNode = document.getElementById("table").firstChild;
This works fine in Firefox ESR (10/17/24) and IE9 but fails in IE10, and the reason appears to be because IE10 is inserting weird DOM entries and it's one of those I'm picking up with firstChild instead of the desired thead. I base this on the DOM dump below along with the fact that tableNode.data is set to the string type.
The DOM in IE10 compatibility mode (where it also works) looks like this:
and you can see that the DOM indeed looks sensible. However, examining the DOM in normal IE10 mode shows this:
For what it's worth, Chrome gives me:
and FF17esr gives me:
neither of which seem to have the empty text elements.
Now, I can run the site in compatibility mode but that's an annoying thing to have to tell all our customers. I can also apparently add the hideous:
<meta http-equiv="X-UA-Compatible" content="IE=9">
to my output but I'm not entirely certain what other effects that may have.
I'd like to understand first why IE10 is adding these nodes whereas IE9/FF/IE10compat aren't. There are some discussions I've found stating that whitespace in the HTML may be causing it but it seems to me that this shouldn't result in random nodes being created and, in any case, I don't think I have any superfluous white space. Although I should mention that the value of tableNode.data mentioned above as type string is actually \n, meaning that the newline at the end of the line may be creating this DOM entry.
But, to be honest, that seems ludicrous. HTML is supposed to ignore whitespace outside of tags as far as I'm aware, or at least fold it into a single element. I find it hard to believe that something like:
<tag>line 1</tag>
<tag>line 2</tag>
would result in three DOM entries, tag, empty node and tag just because there's a newline between them.
Any ideas on how to best solve this? Am I going to have to modify my Javascript code to skip these DOM entries?
You can never know where a browser might insert text nodes so you have to make sure you're getting the first child "element"in case the browser put a text node there.
Here's a simple function that will do that:
getFirstChildElement(parent) {
var node = parent.firstChild;
// advance until we get to an element node (skipping text and comment nodes)
while (node && node.nodeType !== 1) {
node = node.nextSibling;
}
return node;
}
Or, if you just want to get the <thead> element, you can simply use this:
table.getElementsByTagName("thead")[0]
Are you absolutely sure Firefox doesn't show those empty text nodes? I'm asking because it should, if it doesn't then it's a bug in Firefox.
Previously only IE behaved the way you expected. All other browsers including Firefox, Chrome, Safari and Opera followed W3C DOM standards which requires them to retain those whitespace. IE10 now join the ranks of other web browsers and behave in a standards compliant way.
You'd be right to complain that this doesn't make sense but it's what the standards require.
As such, the correct way to get the element is to check it's tagName:
var table = document.getElementById("table");
var child = table.firstChild;
while (child && child.tagName != 'thread') {
child = child.nextSibling;
}
// remember to check child after this point because it may be undefined
Additional explanation
Firebug and Chrome's DOM explorer hides those text elements as a matter of convenience, but it's still there. You can try this out:
<html>
<body>
<div id="foo">
<div id="bar">
</div>
</div>
<script>
var f = document.getElementById('foo');
document.body.innerHTML += f.firstChild.id + <br>;
document.body.innerHTML += f.firstChild.nextSibling.id + <br>;
</script>
</body>
</html>
In all browsers except older versions of IE the above page would output:
undefined
bar
That's because the firstChild is the empty text node. You can console.log it if you like to check out that firstChild.
There's this question, but the solution posted is browser sniffing, which I'm trying to avoid.
I need to make my website compatible with the iPad and perhaps newer Androids. I've been using an old version of the FCKEditor (now CK Editor) for my WYSIWYG editors, but that doesn't work on mobile, so I want to swap it out for a simple textarea with something like markdown, if it's not supported.
Supposedly it won't work because mobile devices tend not to support this contentEditable property, which means the on-screen keyboard won't pop up when you click on it.
How can I detect if the browser supports contentEditable? It's been suggested that I just check the div in question with something like mydiv.contentElement === undefined or something like that, but the div (if it's using one) is all built into the FCK Editor and buried somewhere in an iframe.
Isn't there another way to detect if a browser supports contentEditable in general?
Just tried this:
var contentEditableSupport = typeof $('<div contenteditable="true">')[0].contentEditable !== 'undefined';
Says "true" on my iPad...... I don't think it should.
Edit: It's returning "string" because all attributes are strings.... it's just reading the attribute. How else am I supposed to do this?
You could create an anonymous editable div, check it for contentEditable, then remove the div.
Here is the test I use and is also used in Modernizr. It will give false positives in iOS 4 (and possibly earlier) but unfortunately it's impossible to detect in those environments.
var contentEditableSupport = "contentEditable" in document.documentElement;
I was able to accomplish this by checking the default value of the contentEditable property rather than the presence or type. The W3 spec indicates that the missing value default for contentEditable is "inherit", but in older browsers (e.g. Android/Gingerbread) the default value is "false". Thanks to fudgey for the comment on the OP that pointed me in the right direction!
This test works for me:
var contentEditableSupport = $('<div/>')[0].contentEditable == 'inherit';
Check for execCommand
if (document.execCommand) {
... browser supports contentEditable
} else {
... browser doesn't support contentEditable
}
To check if any propery exits for a element. You can do this
var element = document.createElement('__ELEMENT__');
if ('__PROPERTY__' in element ) {
// property supported in the browser
}
or
if ('__PROPERTY__' in document.createElement('__ELEMENT__') ) {
// property supported in the browser
}
The below link contains it all.
https://github.com/Modernizr/Modernizr/issues/570
http://diveintohtml5.info/everything.html#contenteditable
Have tested in Chrome and Firefox, they both preserve line breaks, but MSIE isn't.
Here is proof:
http://jsfiddle.net/rNHAm/
It's a bug of MSIE or jQuery, or it's ok for both? :)
I can't even get third alert in MSIE...
Thanks :)
IE appears to remove line breaks from the .innerHTML property of the container tag, no matter the format (the .nodeValue property of the text node still contains the original value):
var div = document.createElement("div");
var t = document.createTextNode("one\ntwo\rthree\r\nfour");
div.appendChild(t);
alert(t.nodeValue);
alert(div.innerHTML);
IMHO it looks like an IE bug: in HTML, white space must be collapsed but not completely removed.
I am detecting browsers and trying to apply style,
alert(BrowserDetect.browser);
if(BrowserDetect.browser == "Opera") {
document.getElementById(myBox).style.paddingTop = "5px";
}
Alert Shows the browsers name accurately but why is the style not getting applied?
Try adding an alert inside the if and see if it executes. Chances are, there's whitespace around the Operastring.
Put two copies of the alert() call inside the if statement, one before the style assignment and one after. I think you'll find that the second doesn't happen, for any of several reasons:
The variable myBox doesn't exist
myBox has a value that is not an ID of a valid DOM element
myBox does name a valid element, but it is not a block type element, so padding doesn't apply
You can also try running the same code in another browser with a solid JS debugger, like Firefox + Firebug or Safari with the Develop tools enabled. (Preferences | Advanced.) This may lead you to the problem with that style assignment faster.