Are jQuery created nodes fakes? - javascript

I have been fighting with this for hours, and yet it evades my comprehension...
var newLabel = $('<div></div>');
newLabel.appendTo("#f0");
console.log($("#f0").html()); // <br><div></div>
console.log(newLabel); // [object Object]
var div = newLabel.first().get();
console.log(div); // [object HTMLDivElement]
if( div instanceof HTMLDivElement ) { console.log("VALID"); } //
else { console.log("INVALID"); } // INVALID
console.log(div.appendChild); // undefined
We create a div element using jQuery and append it to a DOM element. It goes in, check. Object prints as "HTMLDivElement", check.
HOWEVER. It fails the instanceof. Also, it should have the method appendChild, but its undefined. (Indeed, it throws an error if I try to call it.)
What on earth is happening here? Is the element jQuery created for us, a fake?

get() returns an array, so div is an array in your case (which obviously is not an HTMLDivElement nor does it have a method appendChild). See: http://api.jquery.com/get/#get2. If you use .get(0) (or just [0]) instead, you'll get the actual div element.

Related

What is the vanilla JS equivalent of jQuery's $(document)?

I'm trying to figure out the vanilla equivalent of the following code:
$(document).attr('key', 'value');
So far I've looked into
document - it's not an element so you cannot call setAttribute on it
document.documentElement - returns the html tag. This is not the same "element" that jquery is targeting
$(document)[0] seems to return a shadow element in Chrome Inspector
$(document).attr('key', 'somethingUnique') doesn't exist in the Chrome Inspector
Is jQuery creating it's own shadow element mock of the document so it can treat it like a real element? What element is jQuery actually referencing when you do $(document)?
A jQuery results set is an array like object that in general holds DOMElement, but jQuery does not really care about what type the objects in the result set have. Neither the DOMElements nor any other element that is stored within the jQuery result set is somehow mocked/wrapped, they are directly stored in the result set. jQuery tries to figure out what it has to do to those objects by looking at their available functions.
When you call .attr, jQuery checks for each object in the set if it has the function getAttribute if this is the case it assumes that it also has a function setAttribute.
If it does not have a function getAttribute, then it will forward the function call to the .prop() function, and prop will internally just do:
elem[name] = value
So if you pass a plain object to jQuery, then it will just set its property.
var a = {
}
$(a).attr('test', 'hello world');
console.dir(a) // for `a` the property `test` is set
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
If you add a getAttribute and setAttribute function to that object then jQuery will call those:
var a = {
getAttribute() {
},
setAttribute() {
console.log('setAttribute was called')
}
}
$(a).attr('test', 'hello world');
console.dir(a);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
However, you should always assume, that the way and order how jQuery does these tests might change. Moreover, only rely on it if it is mentioned explicitly in the documentation.
I believe you're incorrect about $(document) not referring to document, thus, the answer is (quite simply):
document['key'] = 'value'
E.g. In Chrome:
> $(document)[0] === document
true
> $(document).attr('blarg', 'narf')
n.fn.init [document, context: document]
> document.blarg
"narf"
> document.foo = 'bar'
"bar"
> document.foo
"bar"
jQuery is just assigning the value to document directly.
$(document).attr('test', 'hello world');
console.log(document['test']); // prints "hello world"
I really thought jQuery would have wrapped DOM elements, since for some reason, I never write var x = $('#x') to reuse it later but recall $.
That´s why I wrote:
Yes it is wrapped
But after reading #t.niese answer here I tried
var x = $('#x')
var y = $('#y')
var ref = x[0]
x[0] = y[0] // hack jQuery Object reference to DOM element
setTimeout(() => x.html('1'), 1000) // actually writes in #y
setTimeout(() => x.html('2'), 2000) // actually writes in #y
setTimeout(() => { x.push(ref) }, 2500) // hack jQuery Object reference to DOM element
setTimeout(() => x.html('3'), 3000) // actually writes in both #x and #y
And understood I don't write var x = $('#x') not because it is a wrapped object, but exactly because it is not a wrapped object.
I thought the entry point of the API was $, but I may see the API like var api = $(), and the entry point as (el) => api.push(el) or (sel) => api.push(document.querySelector(sel))
I can $().push but I can not $().forEach nor shift nor unshift but yes delete an index, also
In the example
setTimeout(() => { x.map((item) => {console.log(item)}) }, 3500)
logs 0 and 1, not the elements. Tested using jQuery version 3.3.1

uncaught typeerror undefined is not a function on .empty()

I am getting uncaught type error undefined is not a function on .empty() method. The code is
title: "Criteria: "+ (criteriaText.empty() ? (criteriaIncludeText.empty()? criteriaExcludeText : criteriaIncludeText) : criteriaText)
In the above code criteriaText and criteriaIncludeText are having values. I could see that in my alert message. Version of jQuery used is 1.7 . I am not really sure why it is giving uncaught typeerror undefined is not a function when there is proper value. COuld some one please help?
Edit
These variable are getting their values, by following code.
var criteriaText = jQuery(".categorySelect option[value='"+categoryId+"']").text();
var criteriaIncludeText = jQuery(".categorySelectInclude option[value='"+categoryId+"']").text();
var criteriaExcludeText = jQuery(".categorySelectExclude option[value='"+categoryId+"']").text();
You are trying to use jQuery's empty method on a string (instead of a HTML DOM element):
For example:
var criteriaText = jQuery(".categorySelect option[value='"+categoryId+"']").text();
However, jQuery doesn't provide that method on a string so you are trying to execute () a function that doesn't exist (aka, not delivered by jQuery's prototype chain).
It appears you just want to check if the value is empty (in your ternary statement):
title: "Criteria: "+ (criteriaText.empty() ? (criteriaIncludeText.empty()? criteriaExcludeText : criteriaIncludeText) : criteriaText)
However, jQuery's empty() method 'Removes all child nodes of the set of matched elements from the DOM' and returns object jQuery (that evaluates to true):
alert($('#elementId').empty()?'yes':'no');
// object jQuery is an object, object coerces to TRUE
Now, you are in luck, as an empty string already coerced to false, so you can leave off the .empty() part in your ternary, done!
.empty() is jquery function which apply on jquery objects to make them empty like .html('').
Your text variables are not jquery objects but simple strings that will return false when empty, therefore you can try:
title: "Criteria: "+ (criteriaText || criteriaIncludeText || criteriaExcludeText);

Element access on result of getElementsByClassName/querySelectorAll

I'm trying to access to my elements by getElementsByClassName/querySelectorAll function, but I don't understand what happens when I try to get element from the result.
var cells = document.getElementsByClassName('mapAtt');
console.log(cells); // [item: function]
console.log(cells[0]); // undefined
console.log(cells.item(0)); // none
console.log(cells.length); // 0
var cells2 = document.querySelectorAll(".mapAtt");
console.log(cells2); // [item: function]
console.log(cells2[0]); // undefined
console.log(cells2.item(0)); // none
console.log(cells2.length); // 0
I read that return of this two functions isn't an array but a nodeList, but even with the item function, it doesn't work.
cells1 and cells2 show me the right thing, a list with my 22 elements, but I cannot access them with array/nodeList methods.
Elements were created with d3.js, but if I don't say mistakes, it is supposed to create well-boned HTML DOM element. So why doesn't it work?

inline javascript function call - missing ] after element list

I have a code:
var data = $('<span onclick="someFunction(' + element + ');">Element information</span>');
where element is a object.
Also, I have a function defined:
someFunction: function(element) {
console.log(element);
.... // some code
}
But when span element tries to call that function, I get error:
SyntaxError: missing ] after element list
someFunction([object Object]);
When I debug it in Firebug, I see
<span onclick="someFunction([object Object]);">
Element information
</span>
How can I normally call that function with concrete element as an argument?
You will not be able to pass the element as it is converted to string in your concatenation. When an object is converted to string it outputs: [Object object]. This is what you are seeing in your debug.
My suggestion:
You may add the element as data to the span like:
$('span).data('element', element);
And in someFunction retrieve it like:
var element = $(this).data('element');
Another option is to bind to click in Javascript at the place where your element is initialized. Like this
function anotherFunction() {
var element = {};
// Initialize element
...
// I have already got the element initialized, now I can bind the click
$('span').click(function() {
// For your debug and validation
console.log(JSON.stringify(element));
});
}
If you try to build some DOM elements with jQuery, you should do this:
// Wrong: $('<span onclick="someFunction(' + element + ');">Element information</span>');
// Right: build up the element step by step
var data = $('<span/>')
.append(
// build up the child <a> element here
$('<a href="#"/>')
.text("Element information")
//.attr('href',"#") Don't really need this
)
.click(
// Here is a real inline function
function(){
someFunction(element);
}
);
Note: .click in jQuery can be used to assign an event handler for the Click event.
In your original code, you are trying to concatenate a string with an object, which will result in applying toString to that object, converting it to a string:
console.log((new Object()).toString()); // [object Object]
console.log("blah" + (new Object())); // blah[object Object]
In your code, it seems that your object is in fact a jQuery object, but it won't make any differences.
So the resulting "code" used to form onclick is invalid:
someFunction([object Object]);
[ and ] is used to construct an Array in JavaScript, like [1, 2] is an Array with two elements. However [object Object] is an invalid JavaScript syntax so you get the error.
Anyway, this is not a correct way to build up DOM element with events, even with jQuery. The above shown the correct way.
You could also try:
var data = $('<span onclick="someFunction(' + JSON.stringify(element) + ');">Element information</span>');
The function:
someFunction: function(element) {
console.log(element);
.... // some code
}
Should return a JSON Object when called.

Why does $('#id') return true if id doesn't exist?

I always wondered why jQuery returns true if I'm trying to find elements by id selector that doesnt exist in the DOM structure.
Like this:
<div id="one">one</div>
<script>
console.log( !!$('#one') ) // prints true
console.log( !!$('#two') ) // is also true! (empty jQuery object)
console.log( !!document.getElementById('two') ) // prints false
</script>
I know I can use !!$('#two').length since length === 0 if the object is empty, but it seems logical to me that a selector would return the element if found, otherwise null (like the native document.getElementById does).
F.ex, this logic can't be done in jQuery:
var div = $('#two') || $('<div id="two"></div>');
Wouldnt it be more logical if the ID selector returned null if not found?
anyone?
This behaviour was chosen because otherwise jQuery would regularly throw NullReference Exceptions
Almost all jQuery functions return a jQuery object as a wrapper around the Dom elements in question, so you can use dot notation.
$("#balloon").css({"color":"red"});
Now imagine $("#balloon") returned null. That means that $("#balloon").css({"color":"red"});
would throw an error, rather than silently doing nothing as you would expect.
Hence, you just gotta use .length or .size().
This is just how jQuery works.
$("#something")
Object 0=div#something length=1 jquery=1.2.6
$("#nothing")
Object length=0 jquery=1.2.6
You can come close to doing what you want by accessing the length the element, and combine with the ternary operator:
console.log(!!$('#notfound').length); // false
console.log(!!$('#exists').length); // true
var element= $('#notfound').length ? $('#notfound') : $('#exists');
console.log(element.attr('id')); // outputs 'exists'
As to the heart of the question:
Wouldnt it be more logical if the ID
selector returned null if not found?
No, not for the JQuery way of doing things - namely, to support chaining of JQuery statements:
$('#notfound').hide("slow", function(){
jQuery(this)
.addClass("done")
.find("span")
.addClass("done")
.end()
.show("slow", function(){
jQuery(this).removeClass("done");
});
});
Even though notfound doesn't exist this code will run without stopping script execution. If the initial selector returns null, you'll have to add in an if/then block to check for the null. If the addClass, find, end and show methods return null, you'll have to add an if/then block to check the return status of each. Chaining is an excellent way to handle program flow in a dynamically typed language like Javascript.
It returns true because to Javascript it is a defined object therefore not false, and jQuery will always give you a new object regardless of whether the element is found or not - however the array length will be zero, e.g.
$("span").length
If you have no <span>, this will be zero, but it could be 1 or more.
You can write your own plugin to avoid repeated if statements as a Jquery plugin, like I did for this one. It's fairly easy to do:
(function($)
{
/* Checks if a jQuery object exists in the DOM, by checking the length of its child elements. */
$.fn.elementExists = function()
{
/// <summary>
/// Checks if a jQuery object exists in the DOM, by checking the length of its child elements.
/// </summary>
/// <returns type="Boolean" />
return jQuery(this).length > 0;
};
})(jQuery);
Usage:
if ($("#someid").elementExists())
{
}
You could check the .length property of the jQuery object. Like this:
if($("#two").length > 0) { // exists...
} else { // doesn't exist
}
In short, you could think of the jQuery selector return value as a group containing 0..n elements, but never being null.
What you're probably really interested in is $("#two")[0], which will give you the first actual element returned by the selector.

Categories