jQuery's after method not working with newly created elements - javascript

Insofar as I can tell, the following code should work, creating a <div> element, and then creating a <p> element; the expression should result in a jQuery object with two elements:
$("<div>first element's content</div>").after("<p>second element's content</p>");
However, what I get is very different. The documentation (see the heading "Inserting Disconnected DOM Nodes") tells me the above code should result in a jQuery object, grabbing both HTML snippets and building the two DOM elements. But, what I've gotten, in several different versions of jQuery, all above 1.4, is a jQuery object with only 1 node. However, the following code works just fine, returning (what I believe is) the correct jQuery object, two elements inside:
$("<div></div>").after("<p>second element's content</p>");
And this example works as well:
$("<div></div>").after("<p>second element's content</p>").after("<p>third element's content</p>");
It seems the .after() method works fine if the first DOM node being created is empty, but does not when it is not (irrespective of the contents of subsequent DOM nodes being appended to it).
Am I missing something about jQuery's internals, quirky DOM issues and/or JavaScript peculiarities, or is this simply a jQuery bug that's persisted from version 1.4 on through 1.7?
(Here's a meager JSFiddle demonstrating the issue pretty plainly.)

This was a known bug in jQuery < 1.9. See http://bugs.jquery.com/ticket/8759
In jQuery >= 1.9, the following information from the upgrade guide should be noted:
Prior to 1.9, .after(), .before(), and .replaceWith() would attempt to add or change nodes in the current jQuery set if the first node in the set was not connected to a document, and in those cases return a new jQuery set rather than the original set. This created several inconsistencies and outright bugs--the method might or might not return a new result depending on its arguments! As of 1.9, these methods always return the original unmodified set and attempting to use .after(), .before(), or .replaceWith() on a node without a parent has no effect--that is, neither the set or the nodes it contains are changed.

Use add() to add objects to the collection. I use after() more in DOM elements that already exist or that are cached in a variable, but most of the time, if you work with dynamic markup is more practical to use the equivalent insertAfter().
$("<div>first element's content</div>").add("<p>second element's content</p>");
EDIT:
This works...
var $el = $('<div/>', {
text: 'hey there'
}).after('<p>Lorem</p>');

I found I was still sometimes having issues with .add() in place of .after(), so another easy way to get around this bug is to make a throw away wrapper element, .append() the sibling elements, and use .html() to just get the inner contents of the wrapper.
Example:
$('body').append(
$('<span/>').append(
$("<div>first element's content</div>")
).append(
$("<p>second element's content</p>")
).html()
);
This will add the <div> and <p> but discard the outer <span>. I have found this usually works fine with .append() but can have problems with .appendTo()

In jQuery 1.12.4, I found out that using a class selector instead of an ID selector solves this issue.
If you are struggling with
    $("#myelement1").after("<p>test</p>"),
add a unique class to your element and try this:
    $(".myelement1").after("<p>test</p>")

Related

What is the difference between insertAdjacentHtml and insertAdjacentElement?

What is the main difference between element and html?
I used
insertAdjacentElement('beforeend', html);
and it showed error. But worked when using
insertAdjacentHTML('beforeend', html);
Just wondering what the difference was.
insertAdjacentElement() is used to insert an element which is already in the Dom.
You can get this element with getElementById() for example.
https://www.w3schools.com/jsref/met_node_insertadjacentelement.asp
insertAdjacentHtml() is used to insert html code.
https://www.w3schools.com/jsref/met_node_insertadjacenthtml.asp
The insertAdjacentElement method is only compatible with either elements already present in the DOM or new elements created with document.createElement method.
The insertAdjacentHTML however will not work when you are trying to insert DOM elements into another but rather works on strings that can be parsed as XML or HTML which is then added to the parent container.
Although they are pretty similar in that they have more positioning parameters than node.appendchild and element.append methods

JQuery working wierdly for function 'find':

I have the following jQuery line:
$('<html>hi</html>').find('a')
I expect the result to be a wrapped set of one element. However the result is an empty array ([]). Why?
-- EDIT --
For some reason the code below works.
$('<html><div>hi</div></html>').find('a');
Why is this happening?
That's because the html element is stripped when the string is parsed:
> $('<html>hi</html>')
[​hi​​]
i.e. the current collection contains an element that you are trying to find(). As the top-level a element doesn't (and can't) have a descendants the find() call will return an empty collection.
From jQuery documentation:
When passing in complex HTML, some browsers may not generate a DOM that exactly replicates the HTML source provided. As mentioned, jQuery uses the browser's .innerHTML property to parse the passed HTML and insert it into the current document. During this process, some browsers filter out certain elements such as <html>, <title>, or <head> elements. As a result, the elements inserted may not be representative of the original string passed.
edit: The second snippet can find() a element as when the html element is stripped the top-level element of the collection is a div element that does have a descendant.
As in the Documentation of .find() descriped
Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
$('<html>hi</html>')
will just provide an Object of your a-tag.
Demo
If there are multiple anchor-tags inside your html-string you can filter them, e.g.:
var elem = $('<html>hihi</html>');
var filter = elem.filter(function(){
return $(this).attr('href') === "cnn.com";
});
Demo
Edit
When passing in complex HTML, some browsers may not generate a DOM
that exactly replicates the HTML source provided. As mentioned, jQuery
uses the browser's .innerHTML property to parse the passed HTML and
insert it into the current document. During this process, some
browsers filter out certain elements such as <html>, <title>, or
<head> elements. As a result, the elements inserted may not be
representative of the original string passed.
Source: http://api.jquery.com/jQuery/#jQuery2 down to the Paragraph Creating New Elements
So jQuery uses .innerHTML. According to the docs
Removes all of element's children, parses the content string and
assigns the resulting nodes as children of the element.
So the html-string <html>test</html> gets stripped to <a></a>.
When wrapping a div around the anchor, the anchor stays a descendat of an elemnt and therefore gets found by the .find()-function.
You should read the documentation at Jquery docs about find()
$('html').find('a');
Check this jsfiddle

Where does jquery put .val() on a DIV?

jQuery (all versions tested up through 2.1.0) allows me to call .val("some value") on a DIV object to set a value on the DIV. It is not displayed and doesn't show up as an HTML5 data property in Chrome Developer Tools. And yet I can fetch the result later with a call to .val().
For example (from http://jsfiddle.net/X2nr6/ ):
HTML:
<div id="mydiv" style="display: none;">Some text</div>
<div id="debug"></div>
Javascript:
$('#mydiv').val('A value attached .');
$('#debug').text( $('#mydiv').val() );
Displayed result:
A value attached.
Where is the value stored? Not knowing where it is stored makes me worry that I am relying on a hack.
jQuery is just assigning to a value property on the div object (the HTMLDivElement instance for that div), even though it doesn't normally have one. Creating new properties on elements is allowed in every browser I've ever seen, so it works. I wouldn't use val with divs on a regular basis, though.
Here's a non-jQuery example:
var div = document.createElement('div');
console.log('value' in div); // false, divs don't normally have a value property
div.value = 42;
console.log('value' in div); // true, we've created a property on the element
console.log(div.value); // 42
Or the same sort of thing using jQuery:
var $div = $("<div>");
display(typeof $div.prop('value'));
$div.val(42);
display(typeof $div.prop('value'));
display($div.prop('value'));
This business of creating new, custom, non-standard properties on elements is called creating "expando" properties. They can be very handy. (jQuery uses them internally, for instance, to manage the data cache and a few other things — if you look closely at a DOM element you've set data on using data, you'll see a property with a name like jQuery1110028597884019836783; that's the key jQuery uses to find the element's data in jQuery's internal data cache. jQuery doesn't store the data in an expando on the element, because of IE garbage collection issues; it stores the key there, and the data in a JavaScript object.)
It stores it on a value property on the DOM object. You can see if by running your code and then inspecting the element in a DOM inspector. In Chrome, the value property will be listed under div#mydiv in the properties tab.
HTMLDivElement objects don't officially support such a property, so you are relying on a hack.
Use data() to store arbitrary data on an element.
$('#mydiv').data("myCustomValue", 'A value attached .');
Although the above answers are accurate, I'd like to complete something out.
jQuery is designed around the concept of wrapping all HTML elements in the jQuery object. That jQuery object happens to be an array that can hold more than one element.
jQuery also goes out of its way to hide this fact from you so that the average jQuery developer never has to worry about exactly what he has -- simply call the right method and the magic happens.
(You see this if you do a $(".someClassYouHaveLotsOf").hide() or $(".someClassYouHaveNoneOf").hide()`.)
jQuery's val() method is just a wrapper for accessing an HTML input element's value property. Since jQuery doesn't throw errors unless there is really no way what-so-ever, it silently helps you by accessing the value property on whatever HTML element it happens to have. div span or whatever.
In most browsers, this works -- mostly enough.
If you are really interested in setting values on HTML elements for use later, the data() method is far better suited. Straight HTML would use <element>.setAttribute("data-key", "value");
And that is about the only time you'll see me using the HTML attributes over properties, BTW.

jquery .data lost after remove() and append()

Sorry, this seems like a stupid question... but is this actually expected behaviour?
I store data on some element:
$('#source-list li.active').data('relation-text', textEditor.value());
Later the element is moved from one list to another:
$('#source-list li.active').remove().appendTo('#target-list')
Right before 'remove()' 'data()' returns the expected value. After remove(), the data is gone.
I would know how to work around this... but it seems odd to me - is this expected behavior?
I think, so, judging from the Jquery Documentation:
The .data() method allows us to attach data of any type to DOM elements in a way that is safe from circular references and therefore from memory leaks.
Ergo, even though you can still reference it, because the DOM element has been removed, the data associated with it has been removed.
You can use .detach() according to JQuery:
The .detach() method is the same as .remove(), except that .detach() keeps >all jQuery data associated with the removed elements. This method is >useful when removed elements are to be reinserted into the DOM at a later time.
var div = $("div").detach();
$(div).appendTo("body");

Html5 postMessage using jQuery, but not jQuery-postMessage script

So I was messing around with the Html5 PostMessage sample at Html5 Demos and I created a sample jsfiddle to see if I understood how it worked together.
The demo makes use of document.getElementById(...) which I thought could be replaced with the jQuery selector $("#..."), but I got stuck on the because the returned object from the jQuery select does not have access to contentWindow while document.getElementById(...) does.
document.getElementById("frame1").contentWindow.postMessage("Hello from another domain", "http://dl.dropbox.com"); // works
$("#frame1").contentWindow.postMessage("Hello from another domain", "http://dl.dropbox.com"); // no dice
I'm not entirely well versed in jQuery to know which of the many methods to call on the results object from the selector to get back to the result I would see from document.getElementById(...).
$("#frame1") // This a jQuery object that encapsulate the DOM element.
$("#frame1")[0] // this is the DOM element.
//Or
$("#frame1").get(0) // this is the DOM element.
Full code:
$("#frame1")[0].contentWindow.postMessage("Hello from another domain", "http://dl.dropbox.com"); // DICE!
Updated Fiddle
But I find it awkward to use jQuery to select by id then extract the DOM element out of it, and not using jQuery at all. what's wrong with document.getElementById? those 15 extra chars?

Categories