html segment:
<div class="container">
<div id="one">Div #1</div>
<div id="two">Div #2</div>
</div>
if I set $p as a jQuery object, as below:
$p = $('<p>whatever you like</p>');
//$p = '<p>whatever you like</p>';
$('#one').after($p);
$('#two').after($p);
then the result would be:
Div #1
Div #2
whatever you like
While I set $p as htmlString, as below:
//$p = $('<p>whatever you like</p>');
$p = '<p>whatever you like</p>';
$('#one').after($p);
$('#two').after($p);
then the result would be:
Div #1
whatever you like
Div #2
whatever you like
Seems while I use object, jQuery doesn't clone the object, but just move it; while I use htmlString, it create a new object accordingly each time.
I want to know the exact reason. It would be more appreciated if any reference could be provided as well.
Many thanks!
Seems while I use object, jQuery doesn't clone the object, but just move it; while I use htmlString, it create a new object accordingly each time.
Correct. The first time you call after, you put the elements inside the jQuery object into the DOM. The second time, they get moved. This is the standard DOM behavior.
It's documented, somewhat indirectly, on the after page:
If an element selected this way is inserted into a single location elsewhere in the DOM, it will be moved rather than cloned:
This echoes the standard behavior of the DOM methods appendChild and insertBefore.
Note that if you call after on a jQuery set that has multiple elements in it, the elements you're passing in will get moved to after the first target element and then cloned to go after the remaining target elements:
Important: If there is more than one target element, however, cloned copies of the inserted element will be created for each target except for the last one.
Example
This second behavior is jQuery-specific (although with sufficient hand-waving one might argue it's similar to how the DOM handles document fragments and, on more modern browsers, template elements).
When $p is a string '<p>whatever you like</p>' and you append it using .after(), the string is added to the HTML. It's never a jQuery object.
When $p is a jQuery object $('<p>whatever you like</p>'); and you're appending it using one of the append functions like .after() you're moving it around the page. If you don't want to move it, clone it.
$('#one').after($p.clone());
$('#two').after($p.clone());
Demo
Related
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
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.
This is the same question as this:
Referring to a div inside a div with the same ID as another inside another
except for one thing.
The reason there are two elements with the same ID is because I'm adding rows to a table, and I'm doing that by making a hidden div with the contents of the row as a template. I make a new div, copy the innerhtml of the template to my new div, and then I just want to edit bits of it, but all the bits have the same ID as the template.
I could dynamically create the row element by element but it's a VERY complex row, and there's only a few things that need to be changed, so it's a lot easier to just copy from a template and change the few things I need to.
So how do I refer to the elements in my copy, rather than the template?
I don't want to mess up the template itself, or I'll never be able to get at the bits for a second use.
Or is there another simpler way to solve the problem?
It will probably just be easiest when manipulating the innerHtml to do a replace on the IDs for that row. Maybe something like...
var copiedRow = templateRow.innerHTML.replace(/id=/g,"$1copy")
This will make the copied divs be prefixed with "copy". You can develop this further for the case that you have multiple copies by keeping a counter and adding that count variable to the replace() call.
When you want to make a template and use it multiple times its best to make it of DOM, in a documentFragment for example.
That way it doesn't respond to document.getElementById() calls in the "live" DOM.
I made an example here: http://jsfiddle.net/PM5544/MXHRr/
id's should be unique on the page.
PM5544...
In reality, there's no use to change the ID to something unique, even though your document may not be valid.
Browsers' selector engines treat IDs pretty much the same as class names. Thus, you may use
document.querySelector('#myCopy #idToLookFor');
to get the copy.
IDs on a page are supposed to be unique, even when you clone them from a template.
If you dynamically create content on your page, then you must change the id of your newly cloned elements to something else. If you want to access all cloned elements, but not the template, you can add a class to them, so you can refer to all elements with that class:
var clonedElement = template.cloneNode(yes); // make a deep copy
clonedElement.setAttribute("id", "somethingElse"); // change the id
clonedElement.setAttribute("class",
clonedElement.getAttribute("class") + " cloned"
);
To access all cloned elements by classname, you can use the getElementsByClassName method (available in newer browsers) or look at this answer for a more in-depth solution: How to getElementByClass instead of GetElementById with Javascript?
Alternatively, if you have jQuery available, you can do this is far less lines of code:
$("#template").clone().attr("id","somethingElse")
.addClass("cloned").appendTo("#someDiv");
The class lookup is even simpler:
$(".cloned").doSomethingWithTheseElements();
Try to avoid using IDs in the child elements of the cloned structure, as all ids of the cloned element should be changed before adding the clone to the page. Instead, you can refer to the parent element using the new id and traverse the rest of the structure using classnames. Class names do not need to be unique, so you can just leave them as they are.
If you really must use ID's (or unique "name" attributes in form fields), I can strongly suggest using a framework like jQuery or Prototype to handle the DOM traversal; otherwise, it is quite a burden to resolve all the cross-browser issues. Here is an example of some changes deeper in the structure, using jQuery:
$("#template").clone().attr("id","somethingElse")
.addClass("cloned") // add a cloned class to the top element
.find("#foo").attr("id","bar").end() // find and modify a child element
.appendTo("#someDiv"); // finally, add the node to the page
Check out my ugly but functional cheese. I wrote a function that works like getelementbyid, but you give it a start node instead of the document. Works like a charm. It may be inefficient but I have great faith in the microprocessors running today's browsers' javascript engines.
function getelement(node, findid)
{
if (node)
if (node.id)
if (node.id == findid)
return node;
node = node.firstChild;
while(node)
{
var r = getelement(node, findid);
if (r != null)
return r;
node = node.nextSibling;
}
return null;
}
When you copy the row, don't you end up having a reference to it? At that point can't you change the ID?
I have a form, which I want to iterate through. I want to show one fieldset at a time, and then show a "next" and "back" button to go to the next section.
I'm assuming that I start with $('fieldset'); but how do I access individual elements thereafter?
$("fieldset")[i] does not seem to work.
How would I accomplish that with jQuery?
I don't necessarily recommend this, but:
$($('.fieldset')[i]).css(...)
Should work.
If you wrap each call to $('.fieldset')[i] in a new JQuery selector, you create a new JQuery object out of that single item. JQuery objects have the method css that you want. Regular dom objects do not. (That's what you get with $('.fieldset')[i])
From the jQuery documentation:
How do I pull a native DOM element from a jQuery object?
A jQuery object is an array-like
wrapper around one or more DOM
elements. To get a reference to the
actual DOM elements (instead of the
jQuery object), you have two options.
The first (and fastest) method is to
use array notation:
$('#foo')[0]; // equivalent to
document.getElementById('foo') The
second method is to use the get
function:
$('#foo').get(0); // identical to
above, only slower You can also call
get without any arguments to retrieve
a true array of DOM elements.
To get a jQuery wrapper back around the DOM element you just extracted, rewrap it like so:
$( $('#foo')[0] ) //now it's ajQuery element again.
$("fieldset").each(function() {
// code, applied for each fieldset
})
I'm new to Protoype.JS and just testing it a bit because I heard it was good, but I'm stuck quite quickly.
As easy as this is with jQuery, it seems to be the end of the world to get the text in an element. I've tried innerHTML in multiple ways but the only thing I can get is "undefined".
alert($$('.mnu_item').innerHTML);
alert($('content').innerHTML);
None of these work.
Content is a div with id "content" and .mnu_item is an anchor tag with class ".mnu_item".
I don't get what the problem is, probably something stupid but it would be great if somebody could point me in the right direction!
EDIT: I've found that it isn't the innerHTML that doesn't work but it's the class selector. The second line in the code above does work. How can I select an element by its class in the latest Prototype version if this isn't the correct way?
Has the DOM loaded when you run your script? If you're not running this code in a window.onload or by placing it at the end of the body, then the elements by not exist when it runs.
Try placing your script just inside the closing </body> tag.
<body>
<!-- my content -->
<script type="text/javascript">
alert($('content').innerHTML);
</script>
</body>
Also, your first line is selecting correctly, but will return an Array of elements, so innerHTML will be undefined.
To iterate the Array, you can do this:
$$('.mnu_item').each(function(val,i) {
alert(val.innerHTML);
});
or if you want to end up with an Array of the innerHTML values, do this:
var values = $$('.mnu_item').map(function(val,i) {
return val.innerHTML;
});
Make sure the DOM is loaded before you run these tests:
$(document).on('dom:loaded', function () {
/* code to execute after dom has loaded */
})
The first line of code $$('.mne_item') doesn't work because $$ gives back an array of all elements matching the css rule. So $$('.mne_item') gives an array of all dom elements which has the class mne_item. You can ask the first one by using the first method or iterate over all items like this:
$$('.mne_item').each(function(elem) {
// elem is the li elements extended by all Element methods of prototype
});
If you use $ in jQuery, it actually uses a similar pattern but hides the each construct. It just applies the chained method to all elements or just the first.
The second line of code $('content').innerHTML should work. $ is a shortcut for document.getElementById so it should give you a DOM node back. The reason why this doesn't work is there is no node where id = content, probably because the dom isn't loaded yet.
For more info about the methods of prototype look at the api: http://api.prototypejs.org/
Also check the default DOM methods: http://quirksmode.org/dom/w3c_core.html
$('content').innerHTML should work. Check your HTML, ensure the ID is unique.
var text = $$('label[for="display_on_amazon"]').first().textContent;
Above code worked for me.
Regarding, $$('.mnu_item').innerHTML
When you are trying to fetch with class selector, prototype returns array of multiple elments, by using [0] or first() method system will point at the first element in that array, after that you can use innerHtml (to get html inside the element) or textContent (to get text content of that element, native javascript method)