Use of Call method on document.querySelector in JavaScript - javascript

This is the sample code I am working on:
var DomClass = {
Eprcent: ".item__percentage"
};
var EPrsent = function (prcent) {
prcent.forEach(function (cur) {
var ele = document.getElementById("exp-" + cur.id).innerHTML;
var S = document.querySelector.call(ele, 'DomStrings.Eprcent'); //Uncaught TypeError: Illegal invocation
S.innerText = cur.prcnt + "%";
});
}
I am getting this exception: Uncaught TypeError: Illegal invocation at line number 7.
How can I use document method on my HTML from JavaScript code, ID, and class which I want to use that is already present in HTML?
This is a data structure of prcent class:
var Expense = function (id, description, value) {
this.id = id;
this.description = description;
this.value = value;
this.prcnt = 0;
};
I am aware of that we can use directly document query but these HTML are generating dynamically with unique ids. I have to put information according to element id in my HTML. ex document.getElementById("exp-" + cur.id) this line use for identifying the HTML element in which i want to put value
My main concern is that how can I use call method on QuerySelector so that I can change "this" pointer to innerHTML variable? If it's not possible, then why?

querySelector acts on a Document object.
Its signature is querySelector(String selector)
The Document method querySelector() returns the first Element node within the document, in document order, that matches the specified selectors, or group of selectors. If no matches are found, null is returned.
The problem related to document.querySelector.call is because you are providing an invalid this value.
Check you are setting document.getElementById("exp-" + cur.id).innerHTML (String) as the this argument for document.querySelector.call (lines: 5), 6))
The Element innerHTML property is used to get or set a string representing serialized HTML describing the element's descendants.
Therefore, using the call method on querySelector to change this pointer to innerHTML is not possible because that variable is a String and not a Document object.
In the following snippet you can check how we can use querySelector.call (nothing different to using call on any other function):
const serializedHtml = document.getElementById('example').innerHTML;
console.log(typeof serializedHtml, serializedHtml); // -> string "<div id="inner">inner div</div><span>...</span>"
const inner = document.querySelector('#inner');
// equivalent call: same result using `.call` with the document object
const usingCall = document.querySelector.call(document, '#inner');
console.log(inner === usingCall, typeof inner, inner); // -> true object <div id="inner">inner div</div>
// using an empty Document
console.log(document.querySelector.call(new Document(), '#inner')); // -> null
// error code when pointing to innerHTML string
document.querySelector.call(serializedHtml, '#inner'); // -> Error: Uncaught TypeError: Illegal invocation...
#example, #inner {
border: 1px solid;
padding: 8px;
text-align: center;
}
<div id="example">
<div id="inner">inner div</div>
<span>...</span>
</div>
Trying to bind the document.querySelector to another Document instance makes no sense to me because if you had another Document object, you can always invoke directly the querySelector function of the other document.
Check the following example:
const htmlStr = '<div id="container"><div id="inner"></div></div>'
const parser = new DOMParser();
const doc = parser.parseFromString(htmlStr, 'text/html');
// using window.document
console.log(document.querySelector('#inner')); // -> null
// pointing to the correct Document
console.log(document.querySelector.call(doc, '#inner')); // -> <div id="inner"></div>
// but, really not neccessary because you can use the correct Document directly
console.log(doc.querySelector('#inner')); // -> <div id="inner"></div>
Hope all of this helps!
Read more info about document.querySelector, Element.innerHTML, Function.prototype.call and DOMParser

You can use querySelector directly on the element if its a DOM element like
var DomClass ={
Eprcent: ".item__percentage"
};
var EPrsent = function (prcent) {
prcent.forEach(function (cur) {
var ele = document.getElementById("exp-" + cur.id);
var S = ele.querySelector(DomClass.Eprcent);
S.innerText = cur.prcnt + "%";
});
}

Related

Is there any way to set a style for a DOM object with a variable?

I have here some code, in which I am trying to change all of something on a page.
var allElements = document.getElementsByTagName('*');
var tag = '.style.backgroundColor';
var val = 'black';
for (var i = 0; i < allElements.length;i++) {
allElements[i] + tag = val;
}
How can I make this work?
This can be done, but not the way you are doing it. You'll need to pass (as an index, using bracket notation) the CSS property name (a string) into the object returned by accessing the .style property.
Also, don't use .getElementsByTagName() as it's a 25+ year old API that returns a "live" node list, which can hurt performance. Instead, use .querySelectorAll() and loop over the returned collection with the Array.forEach() method, which is an alternative to a traditional for counting loop that is a bit easier to work with since you don't have to manage any loop counters and get direct access to the element you are iterating.
var allElements = document.querySelectorAll('*');
var tag = 'backgroundColor'; // Just store the name of the CSS property
var val = 'black';
// Use the more modern Array.forEach() method of looping
allElements.forEach(function(element){
element.style[tag] = val; // Pass the style object's property name as an index
});
* { color:white; border:2px solid white; }
<div>This is a div<br>that spans two lines.<div>
<p>Here's a paragraph</p>
<h1>And a Heading 1</h1>

Create a unique identifier for a DOM element

I am creating an object that stores various elements and their CSS properties.
The code I have now:
// My object
var cssStorage = {};
function store(element, cssProperty, value) {
// Initialize the (sub-)objects if they don't exist
cssStorage[element.id] = cssStorage[element] || {};
cssStorage[element.id][cssProperty] = cssStorage[element][cssProperty] || {};
// Set the cssProperty to equal the value
cssStorage[element.id][cssProperty] = value;
};
Example:
// My element
var box = document.getElementById("box");
// Let's call the function twice to save to properties
store(box, "display", "block");
store(box, "height", "74px");
Now my Object is populated like so:
cssStorage = {
box: { // <- box is the id of the HTML element <div id = "box"></div>
// The property-value pairs
display: "block",
height: "74px"
}
};
So now, if I type the code in the console:
return cssStorage.box.display; // Returns "block"
As you saw in the first block of code I posted, I used element.id as the element's unique identifier, to be able to use it as shown right above.
My problem is the dependency of my script upon element.id. Some elements of my DOM don't have an id and therefore the function is useless for these elements.
In essence, what I want to achieve is to call the function store when my element doesn't have an ID as follows:
// Some ways to get an element
var box = document.getElementsByClassName("boxes")[0];
var box = document.getElementsByTagName("div")[0];
var box = document.getElementsByName("jack")[0];
// It'll show an error, as the function uses 'element.id' and my element doesn't have one
store(box, "display", "block");
Is there a unique identifier for every node in the DOM?
Something that I could use as the name of:
cssStorage = {
[THE NAME]: {}
};
If not, how can I create a unique identifier for my elements, so that I can use the function as shown above without needing an id, class or other property that my element may not have?
You can easily coin a unique identifier for any element that doesn't yet have one:
var customIDprefix = "__myCustomPrefix__";
var customIDcntr = 0;
function getNextID() {
return customIDprefix + customIDCntr++;
}
And, then you can make sure any element you're using has a unique ID:
function checkID(elem) {
if (!elem.id) {
elem.id = getNextID();
}
}
If you're using ES6, you can also just use a WeakMap or Map object as your CSSStorage mechanism which let the DOM element itself be the key so you don't have to make a string key.
In that case, you'd just do this:
var cssStorage = new Map();
cssStorage[elem] = { // <- elem (your DOM element itself) becomes your key into the cssStorage
// The property-value pairs
display: "block",
height: "74px"
}
You could use an integer to handle a sequence and set the id to elements that does not have it, prefixing to avoid duplicates (for example 'myid' + idSequence++).
Please check if this works. Basically trying to clone the original element and assign it back to the original after adding id with random generator.
function store(element, cssProperty, value) {
if ( element.id == undefined ) {
var clonedElem = element.cloneNode(true);
clonedElem.id = Math.floor((Math.random() * 1000) + 1);
element = clonedElem;
}
// Initialize the (sub-)objects if they don't exist
cssStorage.[element.id] = cssStorage[element] || {};
cssStorage.[element.id][cssProperty] = cssStorage.[element][cssProperty] || {};
// Set the cssProperty to equal the value
cssStorage.[element.id][cssProperty] = value;
};

Pass a variable directly into getElementById using the parameter (JavaScript)

I'm working on my first JavaScript game and I want to display certain attributes inside of < p> tags. The ID of each < p> tag will be equal to "show-" and the name of the attribute (see example below for the attribute "name").
But I'm having trouble getting the syntax right for getElementById. Any suggestions?
<p id="show-name"></p>
<script>
name = "Bob";
function display(attribute) {
putItHere = "'show-"+attribute+"'"
document.getElementById(putItHere).innerHTML = attribute;
}
display(name);
</script>
You need to target the right element. Currently you are targeting 'show-Bob' and not 'show-name' what your trying to do. So first generate the id, from the key name and then assign a value to that element.
var name = "Bob";
function display(key, value) {
var putItHere = "show-"+ key;
document.getElementById(putItHere).innerHTML = value;
}
display('name', name);
note keep in mind that IDs should be unique within the document
However, other way to do that is to target all elements with a specific tag, for instance
<div data-id="name"></div>
<div data-id="name"></div>
<div data-id="name"></div>
<script type="text/javascript">
var name = "Bob";
function display(key, value) {
var putItHere = "show-"+ key;
var elements = document.querySelectorAll('div[data-id="'+key+'"]');
for(var eid in elements) {
var element = elements[eid];
element.innerHTML = value;
}
}
display('name', name);
</script>
note that that this doesn't work in IE7 and below.
Your attribute is Bob. So putItHere = "'show-"+attribute+"'" is equivalent to: putItHere = "'show-"+"Bob"+"'", which makes no sense.
You should get the following error Uncaught TypeError: Cannot set property 'innerHTML' of null.
This should do:
function display(attrname, val){
document.querySelector('#show-'+attrname).innerText=val;
}
display("name","Bob");
Here is the Fiddle to play with.

Object has no method 'appendChild'

When I send a div to a function which creates content to a document fragment it provides me this error when I append the fragment to the div.
Object column_left has no method 'appendChild' '
This is my code:
function progress_bar(parent_div){
var frag = document.createDocumentFragment();
var d = document.createElement('div');
d.className = 'progress_bg';
d.style.width = '90%';
frag.appendChild(d);
parent_div.appendChild(frag);
}
The function is called like this:
var d = document.getElementById('column_left');
progress_bar(d);
Does any one know why i cannot append like this?
Before you call progress_bar(d), #column_left doesn't exist (yet) so d is undefined. Since you're passing in an undefined value into progress_bar you can't call appendChild on it.
In order to fix this, you either have to create #column_left first, or pick a pre-existing element like document.body.
Here is a JSFiddle with the code cleaned up, I made it so it appends to the document's body instead and also sets the inner content something is actually rendered.
If you wanted to do it on #column_left, it would be something like this:
var el = document.createElement('div');
el.id = 'column_left';
document.body.appendChild(el);
progress_bar(el);

javascript object reference by shortcuts

I have very very valuable , so i tried to make shortcuts like this :
LIVE example : http://jsfiddle.net/nqeUN/
var d = "document" ,
t = "getElementByTagName" ,
d = "div" ,
oc = "onclick";
d[t](d)[0].oc = function(){
alert("1");
}
but it's not working , what is the reason? i can see in Google plus api that all the objects are defined like this , as strings , how do they make it work?
There are a couple of problems you need to address
You have two values bound to d: "document" and "div".
It's getElementsByTagName
The getElementsByTagName function needs a DOM entry point not a string. Switch the first d to document
When using dot notation for .oc it will bound to the property oc in stead of the value of the variable oc. Use [] notation instead
Code:
var d = document ,
t = "getElementsByTagName" ,
div = "div" ,
oc = "onclick";
d[t](div)[0][oc] = function(){
alert("1");
}
Working Fiddle: http://jsfiddle.net/nqeUN/1/
Strings will work for properties, but not variable names. You also define d twice, and have the wrong method name. You would be able to do this:
var d = 'document', t = 'getElementsByTagName', div = 'div', oc = 'onclick';
window[d][t](div)[0][oc] = function() { ... }
But this really reduces readability and isn't necessary. You could run your code through a minimizer to get this automatically and still maintain readable dev code.
d is a string, not document.
You should write var d = document to get the actual document object.
However, you should not do this yourself; it makes utterly unreadable code.
Instead, you should develop normal, readable Javascript, then use a minifier (such as Microsoft AjaxMin or Google Closure Compiler) to automatically shrink your code as much as possible in production.
if you replace the values in your example, you'll see:
"document".getElementsByTagName("document").onclick = function() {};
1.) d should be set to the global document reference, not the string 'document'
var d = window.document;
2.) getElementsByTagName returns nodes that match the given tag name that are contained within the given DOM node, so passing 'document' as a string would look for HTML elements named 'document'. you need to find the divs, for example:
d.getElementsByTagName("div"); // All the 'div' elements in the document
3.) For method names to be used as strings, they need to be in brackets
document[ t ]; // document.t won't work, t is not a member
4.) Once you've accessed the nodes you care about, you need to loop through them to add event handlers to each element
var d = document.getElementsByTagName("div"),
i = 0,
len = d.length;
for ( ; i < len; i++ ) {
(function() {
// do something with d[i], the current element in the loop
})(i)
}
hope that helps! cheers.
Because the variable d is a string; and the String object does not have a getElementByTagName method.
Furthermore, your d variable is being redeclared as the string div; so you need to assign that to a different name:
var d = "document" ,
t = "getElementByTagName" ,
e = "div" ,
oc = "onclick";
Then, you need to access the window object, and retrieve the document attribute of it:
window[d]
to retrieve the Document element, and then retrieve the getElementsByTagName method from it (read getElements not getElement)
window[d][t]
You then invoke it and pass it the name of the element, retrieve the first value of the returned array, and assign a function to its onclick attribute:
window[d][t](e)[0][oc] = function () {
alert("1");
};
var d = "document",
t = "getElementsByTagName" ,
div = "div" ,
oc = "onclick";
window[d][t](div)[0][oc] = function(){
alert("1");
}
) document is not a string
) document['getElementByTagName'].call(this, 'div')
) . accessor changed to bracket because oc is a string not a property
) you used to var d twice
) it's getElementsByTagName, plural Elements
Full string madness http://jsfiddle.net/nqeUN/8/
"document"["gg"]()["getElementsByTagName"]("div")["0"]["onclick"] = function(){alert(1);};

Categories