Check if element legally supports the src attribute or innerHTML - javascript

Is there a way to check whether an element can display innerHTML or $.html() (like elems that have a separate closing tag) OR is an element whose is meant to have a src attribute according to the HTML spec such as <img>? I'm looking for fast/reliable way to do this via jQuery or native JavaScript.
Edit: According to the HTML spec, elements not designed to have inner content are called void elements but there are also elements like this <iframe src=url>inner</iframe> that are totally valid.

Unfortunately, there isn't a foolproof way to do this because in Javascript, any element can have those attributes.
Also, as odd at it may seem, almost all HTML elements, including <img>, have an innerHTML attribute, even though it can't really use it!
Your best bet is to make a table that specifies what elements have what.
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style>
</style>
</head>
<body>
<div id="anElementWithInnerHTML"></div>
<img id="anElementWithInnerSRC" />
<script>
var div = document.getElementById("anElementWithInnerHTML");
console.log(div.innerHTML); //Outputs ""
console.log(div.src); //Outputs undefined
var img = document.getElementById("anElementWithInnerSRC");
console.log(img.innerHTML); //Outputs "" (weird right?)
console.log(img.src); //Outputs ""
</script>
</body>
</html>

I suppose you could do
if (typeof element.src !== 'undefined')
if (element.innerHTML ...)
Not totally reliable since any element could add those properties (JSON).
(thanks for the typeof fix, thief)

I found the list of elements that (according to the spec) allow the src attribute:
audio, embed, iframe, img, input, script, source, track, video
So this works to check by name:
function srcAllowed(tag) {
if ( !tag ) { return false; }
var tags = ['audio','embed','iframe','img','input','script','source','track','video'];
return 0 <= $.inArray(tag.toLowerCase(), tags); // boolean
}
This works for getting the appropriate content:
function getContent(elem) {
// #param elem is a selected element like $(this)
// returns empty string if attr() and html() are both are falsey
return elem.attr('src') || elem.html();
}
And this is even safer:
function getContentSafer(elem) {
// #param elem is a selected element like $(this)
// returns empty string if attr() and html() are both are falsey
return srcAllowed(elem.prop('tagName')) ? (elem.attr('src') || elem.html()) : elem.html();
}

Related

To close the tab when found certain visible word on a webpage [duplicate]

What is the difference between textContent and innerText in JavaScript?
Can I use textContent as follows:
var logo$ = document.getElementsByClassName('logo')[0];
logo$.textContent = "Example";
The key differences between innerText and textContent are outlined very well in Kelly Norton's blogpost: innerText vs. textContent. Below you can find a summary:
innerText was non-standard, textContent was standardized earlier.
innerText returns the visible text contained in a node, while textContent returns the full text. For example, on the following HTML <span>Hello <span style="display: none;">World</span></span>, innerText will return 'Hello', while textContent will return 'Hello World'. For a more complete list of differences, see the table at http://perfectionkills.com/the-poor-misunderstood-innerText/ (further reading at 'innerText' works in IE, but not in Firefox).
As a result, innerText is much more performance-heavy: it requires layout information to return the result.
innerText is defined only for HTMLElement objects, while textContent is defined for all Node objects.
Be sure to also have a look at the informative comments below this answer.
textContent was unavailable in IE8-, and a bare-metal polyfill would have looked like a recursive function using nodeValue on all childNodes of the specified node:
function textContent(rootNode) {
if ('textContent' in document.createTextNode(''))
return rootNode.textContent;
var childNodes = rootNode.childNodes,
len = childNodes.length,
result = '';
for (var i = 0; i < len; i++) {
if (childNodes[i].nodeType === 3)
result += childNodes[i].nodeValue;
else if (childNodes[i].nodeType === 1)
result += textContent(childNodes[i]);
}
return result;
}
textContent is the only one available for text nodes:
var text = document.createTextNode('text');
console.log(text.innerText); // undefined
console.log(text.textContent); // text
In element nodes, innerText evaluates <br> elements, while textContent evaluates control characters:
var span = document.querySelector('span');
span.innerHTML = "1<br>2<br>3<br>4\n5\n6\n7\n8";
console.log(span.innerText); // breaks in first half
console.log(span.textContent); // breaks in second half
<span></span>
span.innerText gives:
1
2
3
4 5 6 7 8
span.textContent gives:
1234
5
6
7
8
Strings with control characters (e. g. line feeds) are not available with textContent, if the content was set with innerText. The other way (set control characters with textContent), all characters are returned both with innerText and textContent:
var div = document.createElement('div');
div.innerText = "x\ny";
console.log(div.textContent); // xy
For those who googled this question and arrived here. I feel the most clear answer to this question is in MDN document: https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent.
You can forgot all the points that may confuse you but remember 2 things:
When you are trying to alter the text, textContent is usually the property you are looking for.
When you are trying to grab text from some element, innerText approximates the text the user would get if they highlighted the contents of the element with the cursor and then copied to the clipboard. And textContent gives you everything, visible or hidden, including <script> and <style> elements.
Both innerText & textContent are standardized as of 2016. All Node objects (including pure text nodes) have textContent, but only HTMLElement objects have innerText.
While textContent works with most browsers, it does not work on IE8 or earlier. Use this polyfill for it to work on IE8 only. This polyfill will not work with IE7 or earlier.
if (Object.defineProperty
&& Object.getOwnPropertyDescriptor
&& Object.getOwnPropertyDescriptor(Element.prototype, "textContent")
&& !Object.getOwnPropertyDescriptor(Element.prototype, "textContent").get) {
(function() {
var innerText = Object.getOwnPropertyDescriptor(Element.prototype, "innerText");
Object.defineProperty(Element.prototype, "textContent",
{
get: function() {
return innerText.get.call(this);
},
set: function(s) {
return innerText.set.call(this, s);
}
}
);
})();
}
The Object.defineProperty method is availabe in IE9 or up, however it is available in IE8 for DOM objects only.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
textContent is supported by most browsers. It is not supported by ie8 or earlier, but a polyfill can be used for this
The textContent property sets or returns the textual content of the specified node, and all its descendants.
See http://www.w3schools.com/jsref/prop_node_textcontent.asp
Aside from all the differences that were named in the other answers, here is another one which I discovered only recently:
Even though the innerText property is said to've been standardised since 2016, it exhibits differences between browsers: Mozilla ignores U+200E and U+200F characters ("lrm" and "rlm") in innerText, while Chrome does not.
console.log(document.getElementById('test').textContent.length);
console.log(document.getElementById('test').innerText.length);
<div id="test">[‎]</div>
Firefox reports 3 and 2, Chrome reports 3 and 3.
Not sure yet if this is a bug (and if so, in which browser) or just one of those quirky incompatibilities which we have to live with.
textContent returns full text and does not care about visibility, while innerText does.
<p id="source">
<style>#source { color: red; }</style>
Text with breaking<br>point.
<span style="display:none">HIDDEN TEXT</span>
</p>
Output of textContent:
#source { color: red; } Text with breakingpoint. HIDDEN TEXT
Output of innerText ( note how innerText is aware of tags like <br>, and ignores hidden element ):
Text with breaking point.
Another useful behavior of innerText compared to textContent is that newline characters and multiple spaces next to each other will be displayed as one space only, which can be easier to compare a string.
But depending on what you want, firstChild.nodeValue may be enough.
document.querySelector('h1').innerText/innerHTML/textContent
.querySelector('h1').innerText - gives us text inside. It sensitive to what is currently being displayed or staff that's being hidden is ignored.
.querySelector('h1').textContent - it's like innerText but it does not care about what is being displayed or what's actually showing to user. It will show all.
.querySelector('h1').innerHTML = <i>sdsd</i> Will work* - retrieves full contents, including the tag names.
innerHTML will execute even the HTML tags which might be dangerous causing any kind of client-side injection attack like DOM based XSS.
Here is the code snippet:
<!DOCTYPE html>
<html>
<body>
<script>
var source = "Hello " + decodeURIComponent("<h1>Text inside gets executed as h1 tag HTML is evaluated</h1>"); //Source
var divElement = document.createElement("div");
divElement.innerHTML = source; //Sink
document.body.appendChild(divElement);
</script>
</body>
</html>
If you use .textContent, it will not evaluate the HTML tags and print it as String.
<!DOCTYPE html>
<html>
<body>
<script>
var source = "Hello " + decodeURIComponent("<h1>Text inside will not get executed as HTML</h1>"); //Source
var divElement = document.createElement("div");
divElement.textContent = source; //Sink
document.body.appendChild(divElement);
</script>
</body>
</html>
Reference: https://www.scip.ch/en/?labs.20171214

Why CSS isn't working on innerHTML in Pure JavaScript? [duplicate]

<html>
<style type="text/css">
a {
display: none;
}
</style>
<body>
<p id="p"> a paragraph </p>
google
</body>
<script type="text/javascript">
var a = (document.getElementById('a')).style;
alert(a.display);
var p = (document.getElementById('p')).style;
alert(p.display);
p.display = 'none';
alert(p.display);
</script>
</html>
The first and the second alert display nothing other than a empty string, which I thought should be none and block.
However after the intensionally display setting, the third alert finally alert none.
But Why? How could I retrieve the display property correctly?
Thanks.
The .style.* properties map directly onto the style attribute, not to the applied style. For that you want getComputedStyle.
I'd give serious consideration to toggling .className and separating the presentation from the logic entirely.
You need the computed value of the display property for the element. You can get this as follows. Note that most browsers support window.getComputedStyle() whereas the nearest equivalent in IE is the element's currentStyle property:
var el = document.getElementById('a');
var styleObj;
if (typeof window.getComputedStyle != "undefined") {
styleObj = window.getComputedStyle(el, null);
} else if (el.currentStyle != "undefined") {
styleObj = el.currentStyle;
}
if (styleObj) {
alert(styleObj.display);
}
I'd recommend using a JavaScript library for getting computed style. For example, using jQuery you can retrieve computed style with the css() method...
$("#a").css("display");
The css() method is a cross-browser solution as it internally uses the style object and both the getComputedStyle method and the currentStyle object.
If you can use jQuery, there is a method called .is
To check if something isn't displayed, I'd do ... $('someSelector').is(':visible') ...
This would return false if display attribute is set to None.

How to get an html element's color? [duplicate]

<html>
<style type="text/css">
a {
display: none;
}
</style>
<body>
<p id="p"> a paragraph </p>
google
</body>
<script type="text/javascript">
var a = (document.getElementById('a')).style;
alert(a.display);
var p = (document.getElementById('p')).style;
alert(p.display);
p.display = 'none';
alert(p.display);
</script>
</html>
The first and the second alert display nothing other than a empty string, which I thought should be none and block.
However after the intensionally display setting, the third alert finally alert none.
But Why? How could I retrieve the display property correctly?
Thanks.
The .style.* properties map directly onto the style attribute, not to the applied style. For that you want getComputedStyle.
I'd give serious consideration to toggling .className and separating the presentation from the logic entirely.
You need the computed value of the display property for the element. You can get this as follows. Note that most browsers support window.getComputedStyle() whereas the nearest equivalent in IE is the element's currentStyle property:
var el = document.getElementById('a');
var styleObj;
if (typeof window.getComputedStyle != "undefined") {
styleObj = window.getComputedStyle(el, null);
} else if (el.currentStyle != "undefined") {
styleObj = el.currentStyle;
}
if (styleObj) {
alert(styleObj.display);
}
I'd recommend using a JavaScript library for getting computed style. For example, using jQuery you can retrieve computed style with the css() method...
$("#a").css("display");
The css() method is a cross-browser solution as it internally uses the style object and both the getComputedStyle method and the currentStyle object.
If you can use jQuery, there is a method called .is
To check if something isn't displayed, I'd do ... $('someSelector').is(':visible') ...
This would return false if display attribute is set to None.

Finding if element is visible (JavaScript )

I have a javascript function that tries to determine whether a div is visible and does various processes with that variable. I am successfully able to swap an elements visibility by changing it's display between none and block; but I cannot store this value...
I have tried getting the elements display attribute value and finding if the the element ID is visible but neither has worked. When I try .getAttribute it always returns null; I am not sure why because I know that id is defined and it has a display attribute.
Here is the code of the two different methods I have tried:
var myvar = $("#mydivID").is(":visible");
var myvar = document.getElementById("mydivID").getAttribute("display");
Any guidance or assistance would be greatly appreciated.
Try like this:
$(function () {
// Handler for .ready() called.
if ($("#mydivID").is(":visible")) {
alert('Element is visible');
}
});
FIDDLE
Please make sure to include the jQuery file inside the head tag, as follows
<head>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
If you would like to do this only javascript way you may try
window.getComputedStyle(document.getElementById("mydivID"),null).getPropertyValue('display')
Display is not an attribute, it's a CSS property inside the style attribute.
You may be looking for
var myvar = document.getElementById("mydivID").style.display;
or
var myvar = $("#mydivID").css('display');
Let's take a second to see what .is(":visible") is doing in jQuery, shall we?
Here's a link: https://github.com/jquery/jquery/blob/master/src/css.js#L529
return !jQuery.expr.filters.hidden( elem );
where
jQuery.expr.filters.hidden = function( elem ) {
// Support: Opera <= 12.12
// Opera reports offsetWidths and offsetHeights less than zero on some elements
return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
};
So, it's just checking the offset width and height of the element.
That said, and also worth noting, when jQuery checks to see if an element is hidden (i.e. like when triggering a 'toggle' event), it performs a check on the display property and its existence in the dom. https://github.com/jquery/jquery/blob/master/src/css.js#L43
var myvar = $("#mydivID").is(":visible"); //THis is JQUERY will return true if visible
var myvar = document.getElementById("mydivID").getAttribute("display"); //HERE Display is not a attribute so this will not give desired result.
MY SOLUTION
1.Select the element using QuerySelector
var myvar= document.querySelector('ELEMENT');
2.Check the OffsetWidth and Offsetheight to be greater than 0;
(myvar.offsetWidth > 0 || myvar.offsetHeight > 0)
3.if myvar is Greater than 0 then it's visble else not.

innerHTML side effects?

I'm having some issues with a DOM element reference and I think I've tracked it down to having something to do with updating innerHTML.
In this example, at the first alert, the two variables refer to the same element, as expected. What's strange though is that after updating the innerHTML of the parent element (body), the two variables are supposedly different, despite not touching either.
<html>
<head>
<script type="text/javascript">
var load = function () {
var div1 = document.createElement('div');
div1.innerHTML = 'div1';
div1.id = 'div1';
document.body.appendChild(div1);
alert(div1 === document.getElementById('div1')); // true
document.body.innerHTML += '<div>div2</div>';
alert(div1 === document.getElementById('div1')); // false
};
</script>
</head>
<body onload="load();">
</body>
</html>
Using == instead of === produces the same results. I get the same results in Firefox 3.5 and IE6. Any idea what's causing the second alert to evaluate to false?
WHen you get the innerHTML value of the body, add a string to it and put it back in the body, all elements in the body is recreated from the HTML string. What you have in the variable is a reference to an element that no longer exists in the page.
This is because ...
document.body.innerHTML += '<div>div2</div>';
... is not a true "append" .. it's a full replacement. Granted, the replacement is equal to the old content + the new content, the fact is that it is a new string which new DOM elements are built around.

Categories