What navigator.plugins is evaluated to in JavaScript in IE? - javascript

The book Professional JavaScript for Web Developers gives the following code for detecting if a plugin is installed:
<script type="text/javascript">
//check plugins in non-IE browser
function hasPlugin(name) {
name = name.toLowerCase();
for (var i = 0; i < navigator.plugins.length; i++) {
if (navigator.plugins[i].name.toLowerCase().indexOf(name) > -1) {
return true;
}
}
return false;
}
//check plugins in IE
function hasIePlugin(name) {
try {
new ActiveXObject(name);
return true;
} catch (ex) {
return false;
}
}
//check if the flash plugin exists in a browser
function hasFlash() {
var result = hasPlugin("Flash");
if (!result) {
result = hasIePlugin("ShockwaveFlash.ShockwaveFlash");
}
return result;
}
</script>
The hasPlugin function will return false in IE and navigator.plugins.length is used in a for loop, so I wonder what navigator.plugins is evaluated to in IE. I ran the following code in IE8:
alert(navigator.plugins); //return [object]
alert(navigator.plugins.length); //return 0
alert(navigator.plugins === null); //return false
alert(navigator.plugins === undefined); //return false
//check if it is an empty object
var i = 0;
for (var key in navigator.plugins) {
++i;
}
alert(i); //return 1
navigator.plugins is not evaluated to null, undefined and it has one element, but its length is 0. So what exactly navigator.plugins is evaluated to in IE?

Related

JavaScript If / else statement not return false statement

Function returns true, however if / else statement is logging false result. Any idea where I'm going wrong?
function loginDetails(arrayCheck, value) {
for(i = 0; i < arrayCheck.length; i++){
if(arrayCheck[i] === value){
return true;
}
}
return false;
}
var username = [1,2,3,4,5,6,7,8,9,10];
document.write('Login Details: ', loginDetails(username, 9), '</p>');
if(loginDetails === true) {
document.write('Redirect ....Welcome !!</p>');
} else {
document.write('There seems to be an error please try again !!');
}
loginDetails is a function. You then test to see if it is boolean true. Funnily enough, it never will be!
I presume you actually want to run the function. You will need to cache the result in order not to run it twice:
function loginDetails(arrayCheck, value) {
for(i = 0; i < arrayCheck.length; i++){
if(arrayCheck[i] === value){
return true;
}
}
return false;
}
var username = [1,2,3,4,5,6,7,8,9,10];
var loggedIn = loginDetails(username, 9);
document.write('Login Details: ', loggedIn, '</p>');
if(loggedIn === true) {
document.write('Redirect ....Welcome !!</p>');
} else {
document.write('There seems to be an error please try again !!');
}
What do you mean by if(loginDetails === true) ? This doesn't pass any parameters to loginDetails function.
Instead try if(loginDetails(username, 9) === true). Hope this works. Else store loginDetails(username, 9) in a variable and check whether that variable is true?
loginDetails is a function I suppose you want to check its result to be equal with true.
function loginDetails(arrayCheck, value) {
for(i = 0; i < arrayCheck.length; i++){
if(arrayCheck[i] === value){
return true;
}
}
return false;
}
var username = [1,2,3,4,5,6,7,8,9,10];
var loginDetailsResult = loginDetails(username, 9);
document.write('Login Details: ',loginDetailsResult, '</p>');
if(loginDetailsResult === true) {
document.write('Redirect ....Welcome !!</p>');
} else {
document.write('There seems to be an error please try again !!');
}
You are checking if the reference to the function is equal to true, which will always evaluate to false. A function and a boolean are different types and therefore comparing for strict equality will always return false. I have corrected the code, so that the function is called, and the result of the function is compared, instead of the reference to the function.
function loginDetails(arrayCheck, value) {
for(i = 0; i < arrayCheck.length; i++){
if(arrayCheck[i] === value){
return true;
}
}
return false;
}
var username = [1,2,3,4,5,6,7,8,9,10];
document.write('Login Details: ', loginDetails(username, 9), '</p>');
if(loginDetails(username, 9) === true) {
document.write('Redirect ....Welcome !!</p>');
} else {
document.write('There seems to be an error please try again !!');
}

for...of statement in js - issue with dom collection

I'm trying to learn ES2015 and i am having issue with the small function that i have to parse some DOM element and to find all textnodes and delete them.
And I have this function in simple for loop statement.
function deleteTextNodes(element) {
if(!element) {
throw new Error ("Element doesn't exist")
}
for (let i = 0; i < element.childNodes.length; i ++) {
if (element.childNodes[i].nodeType == 3) {
element.removeChild(element.childNodes[i]);
i--;
} else if(element.childNodes[i].nodeType == 1) {
deleteTextNodes(element.childNodes[i])
}
}
And my try to rewrite function in for...on statement syntax below
function deleteTextNodes(element) {
if(!element) {
throw new Error ("Element doesn't exist")
}
for( elem of element.childNodes ) {
console.log(elem, elem.nodeType, );
if (elem.nodeType == 3) {
element.removeChild(elem);
} else if(elem.nodeType == 1) {
deleteTextNodes(elem)
}
}
return true
}
Probably, second function works fine except one - for...on jump over next one node after deletes textnode or something like this. I fix this issue in, first function by adding i--;
So, the question is how to fix this issue in second function?
Just an alternative suggestion, since the scope is playing around with ES6:
function deleteTextNodes(element) {
if(!element) {
throw new Error ("Element doesn't exist")
}
let coll = [element];
while(coll.length){
[{nodeType:nt}] = ([element,...coll] = coll);
if (nt == 3) {
element.remove();
} else if(nt == 1) {
coll.push(...element.childNodes);
}
}
}
This prevents the need for recursion by continuously adding the childnodes to the collection. [element,...coll] = coll assigns the first element to the element var and assigns the remaining collection to coll.
Not necessarily better, but it shows some nice elements of destructuring.
function deleteTextNodes(element) {
if (!element) {
throw new Error("Element doesn't exist")
}
let removeElement = [];
for (elem of element.childNodes) {
if (elem.nodeType == 3) {
removeElement.push(elem);
} else if (elem.nodeType == 1) {
deleteTextNodes(elem)
}
}
removeElement.forEach(elem =>elem.remove());
return true
}

Javascript workaround for firefox iframe getComputedStyle bug

I'm trying to use the following workaround for the hidden iframe/getComputedSytle firefox bug 548397.
if (/firefox/i.test(navigator.userAgent)){
window.oldGetComputedStyle = window.getComputedStyle;
window.getComputedStyle = function (element, pseudoElt) {
var t = window.oldGetComputedStyle(element, pseudoElt);
if (t === null) {
return {};
} else{
return t;
}
};
}
However in my case I also need getComputedSytle.getPropertyValue i.e. I get the following error:
TypeError: my_window.getComputedStyle(...).getPropertyValue is not a function
How can I add getPropertyValue to the above workaround?
You can just create an empty function:
if (/firefox/i.test(navigator.userAgent)){
window.oldGetComputedStyle = window.getComputedStyle;
window.getComputedStyle = function (element, pseudoElt) {
var t = window.oldGetComputedStyle(element, pseudoElt);
if (t === null) {
return {
getPropertyValue: function(){}
};
} else{
return t;
}
};
}
I think a better solution would be this one
function setFirefoxPolyfill() {
if (/firefox/i.test(navigator.userAgent)){
window.oldGetComputedStyle = window .getComputedStyle;
window.getComputedStyle = function (element, pseudoElt) {
var t = window.oldGetComputedStyle(element, pseudoElt);
if (t === null) {
return element.style;
} else{
return t;
}
};
}
}
in case of null response you just return element styles with all prototyped methods and fields

If page contains specific text then reload (using javascript)

If the text We are sorry but we made a boo boo appears then
Wait 5 seconds
reload
I would like to do this in JavaScript.
Here is an attempt
(function () {
"use strict";
function walkTheDOM(node, func) {
if (node && node.nodeType) {
if (typeof func === "function") {
func(node);
}
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}
}
function filterElementsByContains(elements, string) {
var toStringFN = {}.toString,
text = toStringFN.call(elements),
result,
length,
i,
element;
if (text !== "[object NodeList]" && text !== "[object Array]" && !($() instanceof jQuery)) {
return result;
}
result = [];
if (typeof string === "string") {
string = new RegExp("^" + string + "$");
} else if (toStringFN.call(string) !== "[object RegExp]") {
return result;
}
function getText(node) {
if (node.nodeType === 3) {
text += node.nodeValue;
}
}
length = elements.length;
i = 0;
while (i < length) {
text = "";
element = elements[i];
walkTheDOM(element, getText);
if (string.test(text)) {
result.push(element);
}
i += 1;
}
return result;
}
if(filterElementsByContains([document.getElementsByTagName("table")[0]], /We are sorry but we made a boo boo/).length) {
location.reload();
}
The above could should, I think, work for the text if it appears in a specific place. I want to make it more general - so that the text could appear anywhere on that page.
Also, I would like to know how to add a pause so that, for example, it waits 5 seconds before reloading.
I guess I would add incorporate something like:
setTimeout(
function()
{
//location.reload();
}, 5000);
Just do an indexOf on the body's textContent/innerText property
var content = document.body.textContent || document.body.innerText;
var hasText = content.indexOf("We are sorry but we made a boo boo")!==-1;
if(hasText){
setTimeout(function(){
window.location = "http://www.example.com";
},5000);
}
This may work:
var bodyText = document.body.textContent || document.body.innerText;
var msg = "We are sorry but we made a boo boo";
if (bodyText.indexOf(msg) > -1) {
setTimeout(function() {
location.reload();
}, 5000);
}
--sorry for nearly duplicate answer :\ --
--edit--
Tell me - how can I add a second rule, it's a different way of
phrasing the error message: There was an internal error in our system.
We logged the problem and will investigate it later.
This will check for both messages:
var bodyText = document.body.textContent || document.body.innerText;
var msg = [
"We are sorry but we made a boo boo",
"There was an internal error in our system. We logged the problem and will investigate it later."
];
var flag = false;
for (var i = 0; i < msg.length; i++) {
if bodyText.indexOf(msg[i]) {
flag = true;
}
}
if (flag) {
setTimeout(function() {
location.reload();
}, 5000);
}
Explanation: All I did was modify msg to be an array of strings rather than a string itself. Then for every msg we want to check, we loop through the values and compare msg against bodyText. If bodyText contains one of the msg's, we set a flag to true, and then perform an if statement on flag.
If you want to check anywhere in the page... then you have to do just that. Get every DOM element and check if there is your String there... I do not think there is another way.
Yes using setTimeout will do the trick for waiting before reload.

Cannot call method ... of undefined

So I have made this code, it works almost perfectly. The one thing is that setupListener method returns a error at the end, probably it finds a property I don't want to find. I've been struggling with this for a while, but I clearly don't have that much experience with js to solve it. I have setup some console.log methods for debugging. You use this function like this: setupListener(Id or Class or a Tag name as string, event to watch on as string, and an action);
eg. setupListener('.pun', 'click', function (e){ console.log(e); });
var getElement = function (onStr) {
if (onStr.indexOf('.') === 0) {
onStr = onStr.slice(1);
return document.getElementsByClassName(onStr);
}
if (onStr.indexOf('#') === 0) {
onStr = onStr.slice(1);
return document.getElementById(onStr);
}
if (onStr.indexOf('#') !== 0 && onStr.indexOf('.') !== 0) {
return document.onStr = document.getElementsByTagName(onStr);
}
};
var setupListener = function (elementStr, eventStr, action) {
var tempElement = getElement(elementStr);
// element a collection and has addEventListener method
if (tempElement.length > 1 && tempElement[1].addEventListener) {
for (var i = 1; tempElement.length >= i; i++) {
if (typeof(tempElement[i].addEventListener) !== "undefined")
console.log('1'); //debugging
tempElement[i].addEventListener(eventStr, action);
}
}
// IE < 9 Support
// element a collection and has NOT addEventListener method
else if (tempElement.length > 1 && !tempElement[1].addEventListener) {
for (var i = 1; tempElement.length >= i; i++) {
if (typeof(tempElement[i].addEventListener) !== "undefined")
console.log('2'); // debugging
tempElement[i].attachEvent(eventStr, action);
}
}
// element not a collection and HAS addEventListener method
else if (!tempElement.length > 1 && tempElement.addEventListener) {
console.log('3'); // debugging
tempElement.addEventListener(eventStr, action);
}
// element not a collection and has NOT addEventListener method
// IE < 9 support
else if (!tempElement.length > 1 && !tempElement.addEventListener) {
console.log('4'); // debugging
tempElement.attachEvent(eventStr, action);
}
else {
console.log('5'); // debugging
}
};
Your problem is in the part of the collection of elements. Note that you get the first element of the array by array[0] so you get the last element by array[array.length-1]. But what you are doing when you are iterating over the arrays is, starting at 1 and iterating to array.length. When you try to access an unavailable array-element (as array[array.length]) you will get undefined, and thats ecaxtly what your error says. So you have to change the bounds in the for-loops. I.e.
for (var i = 0; i < tempElement.length; i++)

Categories