JavaScript: addEventListener not running - javascript

Learning Javscript and trying to trigger a click event.
I'm not sure what I'm doing wrong but the following doesn't seem to work.
JSFiddle
http://jsfiddle.net/73e7H/1/
HTML
<ul>
<li id="nav-item-39">
Visit
</li>
<ul>
JS
var $visit = document.getElementById('nav-item-39').firstChild;
$visit.addEventListener('click', function() {
print('hello');
});

The firstChild is a text node containing the whitespace after the end of the li start tag and the beginning of the link. You need the first child that's an element.
On many browsers, that's firstElementChild, but if you need support for older browsers you may need a loop.
Fiddle (using alert rather than print)
Another option is to use querySelector, which is available on all modern browsers (even IE8 — but of course, IE8 doesn't have addEventListener):
var $visit = document.querySelector('#nav-item-39 a');
$visit.addEventListener('click', function() {
print('hello');
});
That finds the first a element that's a descendant of #nav-item-39. If you want to require that it's a direct child instead, use the selector string "#nav-item-39 > a" instead.
Fiddle
(Just for completeness: querySelector finds the first matching element and returns it [or null if no match]. There's also querySelectorAll which finds all matching elements and returns a static NodeList.)
Side note: print, unless you override it (and I don't think you do in the fiddle), is window.print which opens the browser's Print dialog.

Try Using firstElementChild instead of firstChild.
firstElementChild will defenitely return element, when firstChild can return text node also.

Related

querySelector() - first inner element

I'm using SVG.js select() function which uses querySelector() function.
Currently, the command I use is: select("[id='1']") (1 could be replaced by some other number)
What I'd like to do is to select the first inner element inside this element. Alternatively, I could select it by tag name.
How to do it?
I tried select("[id='1']:first") but received an error.
By the way, the reason I select it like that is that apparently querySelector has a problem with id's which are numbers.
:first is a jQuery thing. For what you're doing, you can use :first-child, which is a CSS thing:
select("[id='1'] > :first-child");
That selector matches all elements that are the first child of elements with id="1", but if select is using querySelector under the covers, you'll get the first such element.
Note that the > in that is the child combinator: It means we're looking for :first-child within [id='1']. (An earlier version of this answer used [id='1'] :first-child, which uses a descendant combinator [just whitespace]. It would matter for selecting a list of elements, but not if selecting only on the first match.) (You need one or the other, since without any combinator ([id='1']:first-child) it would b elooking for the first [id='1'] that was also a :first-child.)
"I'm using SVG.js select() function which uses querySelector() function."
But your comment under TJ's answer suggests it uses querySelectorAll(). There's a difference.
"What I'd like to do is to select the first inner element inside this element."
If it does use querySelector, then use this selector:
"[id='1'] > *"
That'll give you the first child element inside the [id='1'] element.
But if it actually uses querySelectorAll, then using TJ's :first-child selector will work, but as he noted, you need to be aware that it will return all elements that are the first child of their parent.
You can use the > child selector to ensure just one.
"[id='1'] > :first-child"
"Alternatively, I could select it by tag name. How to do it?"
I don't know which element you're referring to, but in general, include the tag name if the selector is selecting on attribute or position. That'll greatly help the engine to narrow down the set of elements.
// querySelector // querySelectorAll
"div[id='1'] > p" ... "div[id='1'] > :first-child"
"I tried select("[id='1']:first") but received an error."
As TJ noted, that's an invalid selector. jQuery's selector engine is non-conforming to the standards in several different ways. Keep your selectors pure as much as possible so that you don't get hooked on needless dependencies.
"By the way, the reason I select it like that is that apparently querySelector has a problem with id's which are numbers."
You can select by numbers if you escape the leading number.
"#\\1 > *"

Does JavaScript/jQuery always have the latest element or version of that element?

I am writing a userscript that grabs an element with something like:
var theElement = $('div.someClass:last');
To grab the last element in the class .someClass so I can parse it.
This is where my question comes in. There is another script on this page dynamically adding a new <div class="someClass"> every once in a while. I want to always have the last element on the page with a class of .someClass selected.
Will Javascript/jQuery always have the latest element or will I have to manually "refresh" it?
Sushanth's answer is correct, but according to this article you could use
var theElements = document.getElementsByClassName('someClass');
and then reliably use
var theElement = $(theElements[theElements.length - 1]); // wrapping in $() is optional
which is worth doing as $(selector) is quite an expensive operation to perform.
edit - only for ie9 and above though http://caniuse.com/getelementsbyclassname
Your selector is evaluated and the result is returned. If you want to do what you're asking, you'll have to re-evaluate that selector.
Nope .. It will not be automatically refreshed.
Every single time you modify something directly on the selector it is a good idea to cache it again.
The selector you wish is not a live Node list.
In such cases if there seem to any changes in the selector I prefer not to cache at all in the first case.
So that I can directly use the selector and not the cached one.

jQuery's after method not working with newly created elements

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>")

First element of NodeList in document.body.childNodes

There is HTML page with contents like this.
Documentation at MDN says that childNodes returns a collection of child nodes of the given element which is a NodeList.
So, according to the doc, the first child for the NodeList should be <h1>PyCon Video Archive</h1>.
But, in Developer Tools (Chromium), it says the other way.
![enter image description here][2]
So, why exactly the first node is not <h1>PyCon Video Archive</h1>?
Why a text object as first element?
I would appreciate some help here.
EDIT
So, I just figured out that in Firebug (FF), the same function behaves differently.
My new question: Is using .childNodes() an unreliable way of accessing DOM elements?
To get the first element child, you can use...
document.body.firstElementChild;
...but older brwosers don't support it.
A method that has greater support is the children collection...
document.body.children[0];
...which has pretty good support but still has some holes in terms of older browsers.
(Just double checked, and as long as you don't support Firefox 3, and as long as you don't include HTML code comments in the markup, using .children will be safe.)
To ensure that you have the widest browser support, create a function...
function firstElementChild( parent ) {
var el = parent.firstChild;
while( el && el.nodeType !== 1 )
el = el.nextSibling;
return el;
}
and use it like this...
var h1 = firstElementChild( document.body );
Because there's a white-space text-node before the h1 element. Presumably, in the source (if you view source), the h1 opening tag's been either indented, or moved to a new line within the body (or both) in order for readability. At a guess, I'd imagine that it's something like the following:
<body>
<h1>PyCon Video Archive</h1>
<!-- ...other html... -->
If you revise that to:
<body><h1>PyCon Video Archive</h1><!-- ...other html... -->
Then the first childNode will, indeed, be the h1 element.
It's worth noting that text, even outside of an element tag, is still a child-node of the parent element. Albeit one that can't be easily targeted with a selector.

Prototype.js get text from an element

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)

Categories