nextElementSibling Polyfill gives error 'Element' is undefined - javascript

I'm using IE in compatibility mode to support old web pages and also writing new pages.
I'm trying to use nextElementSibling so I've been adding the following Polyfill for it into my JS file. My goal is that if 'nextElementSibling' isn't supported then it will use the definition in the Polyfill.
Here's the Polyfill from the Mozilla site:
if (!("nextElementSibling" in document.documentElement)){
Object.defineProperty(Element.prototype, "nextElementSibling", {
get: function(){
var e = this.nextSibling;
while(e && 1 !== e.nodeType)
e = e.nextSibling;
return e;
}
});}
When I run this I get the error "'Element' is undefined."
I thought Element.prototype was built-in functionality.
Do I need to add something else before adding that Polyfill?
How do I get this working?
Any (relevant on topic) help would be greatly appreciated.
Update
Per a suggestion I tried the following but it's not executing the redefined nextElementSibling. Here's the code I put at the beginning of my JS file:
if (!("nextElementSibling" in document.documentElement))
{
if (!window.Element)
{
Element = function() { }
Element.prototype.nextElementSibling = function()
{
var e = this.nextSibling;
while (e && 1 !== e.nodeType)
e = e.nextSibling;
return e;
}
Element.prototype.firstElementChild = function()
{
var el = this.firstChild;
while (el && el.nodeType !== 1)
{
el = el.nextSibling;
}
return el;
}
var __createElement = document.createElement;
document.createElement = function(tagName)
{
var element = __createElement(tagName);
for (var key in Element.prototype)
element[key] = Element.prototype[key];
return element;
}
var __getElementById = document.getElementById;
document.getElementById = function(id)
{
var element = __getElementById(id);
for (var key in Element.prototype)
element[key] = Element.prototype[key];
return element;
}
}
}
Later in my JS file I do this:
var temp = this.parentElement.parentElement.nextElementSibling;
It doesn't work. The defined nextElementSibling never gets run and temp = undefined.

Related

Web Components with angular.js & IE11 compatibility

I have angular.js application in which I would like to initialize a web component.
It works perfectly fine on other browsers but it seems that IE11 has issues with
document.importNode
angular.js onInit function:
vm.$onInit = function() {
vm.params = $location.search();
if(vm.params.merchantId && vm.params.apiToken && vm.params.amount && vm.params.orderId) {
vm.mid = vm.params.merchantId;
vm.apiToken = vm.params.apiToken;
vm.amount = vm.params.amount;
vm.orderId = vm.params.orderId;
let template = document.querySelector('#template');
let clone = document.importNode(template.content, true);
let cmp = clone.querySelector('cmp-request');
cmp.setAttribute('mid', vm.mid);
template.replaceWith(clone);
}
}
HTML:
<template id="template">
<cmp-request></cmp-request>
</template>
Is there any other way to clone web component and pass params inside without using importNode so it would work on IE11?
IE 11 doesn't support importNode and replaceWith. For importNode, I use children to get <template>'s children to get the web component in IE 11. For replaceWith, I use this polyfill to support it in IE 11.
You can refer to my code sample with dummy values:
function ReplaceWithPolyfill() {
'use-strict'; // For safari, and IE > 10
var parent = this.parentNode,
i = arguments.length,
currentNode;
if (!parent) return;
if (!i) // if there are no arguments
parent.removeChild(this);
while (i--) { // i-- decrements i and returns the value of i before the decrement
currentNode = arguments[i];
if (typeof currentNode !== 'object') {
currentNode = this.ownerDocument.createTextNode(currentNode);
} else if (currentNode.parentNode) {
currentNode.parentNode.removeChild(currentNode);
}
// the value of "i" below is after the decrement
if (!i) // if currentNode is the first argument (currentNode === arguments[0])
parent.replaceChild(currentNode, this);
else // if currentNode isn't the first
parent.insertBefore(currentNode, this.nextSibling);
}
}
if (!Element.prototype.replaceWith)
Element.prototype.replaceWith = ReplaceWithPolyfill;
if (!CharacterData.prototype.replaceWith)
CharacterData.prototype.replaceWith = ReplaceWithPolyfill;
if (!DocumentType.prototype.replaceWith)
DocumentType.prototype.replaceWith = ReplaceWithPolyfill;
var template = document.querySelector('#template');
if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > -1) { //IE11
var clone = template.children[0];
clone.setAttribute('mid', 'test');
} else {
var clone = document.importNode(template.content, true);
var cmp = clone.querySelector('cmp-request');
cmp.setAttribute('mid', 'test');
}
template.replaceWith(clone);
<template id="template">
<cmp-request></cmp-request>
</template>

javascript - how to take selected element from function and pass it through as a parameter of another function

I have been trying for days upon weeks with trying to build a personal library without jQuery for my school club, and so far I am hitting a rut when it comes to passing through an element or objects through to another function. The notation I am trying for is this :
CC(function(){
CC('id:wrapper').set('html','Hello World!');
});
That is my test code, and the library looks as it does below:
"use strict";
var CC = function () {
var args = arguments[0] || {};
if(typeof args === "object") {
args = args || {};
}
else if(typeof args === "function") {
args = arguments[0];
return window.onload = args;
}
else if(typeof args !== "object" || typeof args !== "function") {
var elem = get(args);
return elem;
}
};
CC({
//Can only be done once. Will return TypeError because '$' won't exist afterward
noConflict : function (name) {
name = new CC();
return name;
}
});
//The way to modify things
CC.mod = CC.prototype = {};
CC.extend = CC.mod.extend = function () {
var args = arguments[0] || {};
var target = get(args);
return target;
};
CC.mod.extend({
//Use psuedo types to set specific values (required)
set : function(type, value) {
return set(this.target, type, value);
}
});
//General get function to get selectors, generate functions, or return values
function get() {
var args = arguments[0] || {};
//Check if the argument is a function
//If it is, return the function on page load
if (typeof args === "function") {
return window.onload = args;
}
//Check argument type
if(typeof args !== "object") {
args = arguments[0];
return args;
}
else {
args = {};
return args;
}
//Check if args has an elem psuedo
if(args.indexOf("id:") > -1 || args.indexOf("class:") > -1 || args.indexOf("tag:") > -1) {
var target = args;
//Run id psuedo
if(target.indexOf("id:") > -1) {
target = target.replace('id:','');
console.log(target);
return document.getElementById(target);
}
//Run class psuedo
else if(target.indexOf("class:") > -1) {
target = target.replace('class:','');
console.log(target);
return document.getElementsByClassName(target);
}
//Run tag psuedo
else if(target.indexOf("tag:") > -1) {
target = target.replace('class:','');
console.log(target);
return document.getElementsByTagName(target);
}
}
//Check if args is not null
//If not, then return args value
if(args !== null) {
return args.value;
}
else {
return null;
}
}
//General function to set things for elements
function set(elem, property, value) {
//If the element provided is part of getting an element
//If it is, run the psuedo checker
if(elem.indexOf("id:") > -1 || elem.indexOf("class:") > -1 || elem.indexOf("tag:") > -1) {
elem = get(elem);
//Rerun the set() function to set properties and values
set(elem, property, value);
}
//If not, then run the type psuedo checker
else {
//Check if style
if(property.indexOf("css:") > -1 || property.indexOf("style:") > -1) {
//Check for the independent types
if(property.indexOf("css:") > -1) {
property = property.replace('css:','');
return elem.style[property] = value;
}
else if(property.indexOf("style:") > -1) {
property = property.replace('style:','');
return elem.style[property] = value;
}
}
//Check if attribute
else if(property.indexOf("attr:") > -1) {
property = property.replace('attr:','');
return elem.setAttribute(property, value);
}
//Check if html
else if(property.indexOf("html") > -1) {
return elem.innerHTML = value;
}
//To add more, just add another else if(condition...) {Code} statement
//Condition must be defined in psuedo selectors
//Condition must be property.indexOf("selector:" > -1)
//return statement must consist of a return value from the value parameter
}
}
I don't know how to get my methods to pass through correctly and I don't know how to get my methods to apply to the element in the CC('id:wrapper') code. I already have the 'psuedo selector' made to get rid of the id: code. Any help would be much appreciated!
You've posted quite some code which I wasn't able to get to work quickly, so I'm not sure if this will help you out.
The basic idea is that your CC method will always have to return an object with a set method. If there's no element with id="wrapper", you'll have to figure out a way to handle exceptions.
You can use bind to create a new function from an earlier defined function with a pre-bound this context and pre-filled in arguments.
A simplified example:
var CC = function(query) {
return {
set: set.bind(null, document.querySelector(query))
};
}
function set(element, attr, val) {
element.setAttribute(attr, val);
}
CC("input").set("placeholder", "I was set by js");
<input type="text" />
If you want to do more advanced binding of arguments, I'd suggest you google "Currying". With some code, you can make functions automatically return new functions when called with less arguments than needed.
What .bind does:
The bind method is defined in Function.prototype. You can call it on any function you've defined to create a new function.
The first argument that goes in to bind, is used as the this context in the newly created function. You could, for example, do:
var myDiv = document.querySelector("div");
var logText = function() {
console.log(this.innerText);
};
var logDivText = logText.bind(myDiv);
logText(); // Bound to window, logs undefined
logDivText(); // Bound to div, logs text
<div>Text in a div</div>
Any other arguments passed to bind, are automatically passed as arguments. For example:
var sum = function(a, b) {
return a + b;
};
var sum3 = sum.bind(null, 3); // we don't use this, so we don't define it
console.log(sum3(5)); // Prints 8

How do I invoke my function on keyup?

I have a JavaScript function that I want to fire once the user enters text inside an input element. Currently I can only see the function firing if I console.log it. How do I get it to fire using keyup method?
The relevant code is below.
var $ = function (selector) {
var elements = [],
i,
len,
cur_col,
element,
par,
fns;
if(selector.indexOf('#') > 0) {
selector = selector.split('#');
selector = '#' + selector[selector.length -1];
}
selector = selector.split(' ');
fns = {
id: function (sel) {
return document.getElementById(sel);
},
get : function(c_or_e, sel, par) {
var i = 0, len, arr = [], get_what = (c_or_e === 'class') ? "getElementsByClassName" : "getElementsByTagName";
if (par.length) {
while(par[I]) {
var temp = par[i++][get_what](sel);
Array.prototype.push.apply(arr, Array.prototype.slice.call(temp));
}
} else {
arr = par[get_what](sel);
}
return (arr.length === 1)? arr[0] : arr;
}
};
len = selector.length;
curr_col = document;
for ( i = 0; i < len; i++) {
element = selector[i];
par = curr_col;
if( element.indexOf('#') === 0) {
curr_col = fns.id(element.split('#'[1]));
} else if (element.indexOf('.') > -1) {
element = element.split('.');
if (element[0]) {
par = fns.get('elements', element[0], par);
for ( i =0; par[i]; i++) {
if(par[i].className.indexOf(element[1]> -1)) {
elements.push(par[i]);
}
}
curr_col = elements;
} else {
curr_col = fns.get('class', element[1], par);
}
} else {
curr_col = fns.get('elements', element, par);
}
}
return elements;
};
You need to bind your method to the keyup event on the page.
You could try
document.addEventListener('keyup', $)
Or assuming you have the input element as element you could do
element.addEventListener('keyup', $)
Your function will be passed the event which you could use to investigate the state of the element if you needed that information to trigger or not trigger things in the function.
Here's a quick sample where the function that get's run on keypress is changeColor.
var COLORS = ['red', 'blue','yellow', 'black']
var NCOLORS = COLORS.length;
function changeColor(ev) {
var div = document.getElementById('colored');
var colorIdx = parseInt(Math.random() * NCOLORS);
console.log(colorIdx);
var newColor = COLORS[colorIdx];
div.style.color = newColor
console.log("New color ", newColor)
}
document.body.addEventListener('keyup', changeColor)
Though I'm not using the event (ev), I like to show, in the code, that I expect that variable to be available.
See it in action here - http://codepen.io/bunnymatic/pen/yyLGXg
As a sidenote, you might be careful about calling your function $. Several frameworks (like jQuery) use that symbol and you may run into conflicts where you're overriding the global variable $ or where the framework overrides your version if it.

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.

javascript: wait until xpath exists with timeout?

Basically, I want to wait for an element to appear (e.g. dynamically loaded element). I want to know how I can edit it to support xpath,different DOM documents (e.g. dynamically loaded iframe) and also set a timeout value so that it will quit if after X number of seconds, the element is till not found, throw an error that I can handle.
waitUntilExists(xpath,document,5,function(){
// Element exists, if after 5 seconds and it doesn't exist handle it here.
})
original code to modify:
(function(){
var _waitUntilExists = {
pending_functions : [],
loop_and_call : function()
{
if(!_waitUntilExists.pending_functions.length){return}
for(var i=0;i<_waitUntilExists.pending_functions.length;i++)
{
var obj = _waitUntilExists.pending_functions[i];
var resolution = document.getElementById(obj.id);
if(obj.id == document){
resolution = document.body;
}
if(resolution){
var _f = obj.f;
_waitUntilExists.pending_functions.splice(i, 1)
if(obj.c == "itself"){obj.c = resolution}
_f.call(obj.c)
i--
}
}
},
global_interval : setInterval(function(){_waitUntilExists.loop_and_call()},5)
}
if(document.addEventListener){
document.addEventListener("DOMNodeInserted", _waitUntilExists.loop_and_call, false);
clearInterval(_waitUntilExists.global_interval);
}
window.waitUntilExists = function(id,the_function,context){
context = context || window
if(typeof id == "function"){context = the_function;the_function = id;id=document}
_waitUntilExists.pending_functions.push({f:the_function,id:id,c:context})
}
waitUntilExists.stop = function(id,f){
for(var i=0;i<_waitUntilExists.pending_functions.length;i++){
if(_waitUntilExists.pending_functions[i].id==id && (typeof f == "undefined" || _waitUntilExists.pending_functions[i].f == f))
{
_waitUntilExists.pending_functions.splice(i, 1)
}
}
}
waitUntilExists.stopAll = function(){
_waitUntilExists.pending_functions = []
}
})()

Categories