adding onclick event on class - javascript

I am trying to write a greasemonkey script which adds an onClick listener on a button (define below) and do some specific things. I was trying to get this button obj using document.getElementsByTagName but it returned null. I tried to iterate over document.anchors and it returned null as well. how can i get this element object and add onclick event to it.
<a class="editicon" aria-label="Edit this field" role="button" href="#"><img src="https://url-to/images/edit.png?3423434"></a>
There is already an onclick added to this object, I don't want to replace it.
UPDATE
Adding my Greasemonkey script
// ==UserScript==
// #name cr
// #namespace my_namespace
// #version 1
// #grant none
// ==/UserScript==
(function(){
console.log('test ...')
var editicon = document.getElementsByTagName('editicon');
console.log(editicon);
})();

First, your question talks about a button, but your code does not include one. Instead of using an <a> element and then disabling its native navigation function with href="#", it would be semantically better to use an actual <button> element.
Second, you should not use inline HTML event attributes (onclick, onmouseover, etc.) as they:
Create "spaghetti code" that doesn't scale, is hard to read, leads to duplication and doesn't follow the "separation of concerns" methodology.
Create global anonymous wrapper functions around your event attribute value that alter the this binding of your code.
Don't follow the W3C Event Standard of using the addEventListener() API.
Now, there are several ways to get a reference to a DOM element and some are better than others depending on your HTML structure and how many elements you are trying to get. document.getElementsByTagName() returns a "node list" of all the elements that were found. Even if no elements were found, you still get this list as the returned object (the list will just be empty when no elements were found). To extract a particular element from the result, you'll need to pass an index into that list, which is an "array-like" object. Additionally, getElementsByTagName returns a "live" node list, meaning that it will re-scan the document upon every interaction to ensure that the most current list is provided. While this can be beneficial in some circumstances, it comes with a performance cost.
This, for example, would extract the first a element in the document:
var myElement = document.getElementsByTagName("a")[0];
But, since you are only expecting a single element, that is overkill. To get just one element, and if that element has an id attribute on it, you can/should use: document.getElementById("theIdHere"); as getElementById() is generally the fastest way to find a single element in your HTML structure.
Additionally, there are other ways to get an element or elements, like querySelector() and querySelectorAll(), which allow you to use CSS selector syntax in your JavaScript queries.
For your code, see the following snippet:
// Get a reference to the first <button> element in the document
var b = document.querySelector("button");
// Or, if the element has an id, the best solution would be:
var b = document.getElementById("btn");
// Add a click event handler to the element
b.addEventListener("click", handleClick);
// Function that will be called when anchor is clicked
function handleClick(){
alert("You clicked me!");
}
button { background-color:rgba(0,0,0,0); }
img { width: 50px; }
<button class="editicon" aria-label="Edit this field" role="button" id="btn"><img src="https://s-media-cache-ak0.pinimg.com/736x/5a/42/c6/5a42c6224e3ce7fa9837965270bfcdd9--smiley-face-images-smiley-faces.jpg"></button>

Related

Understanding element types

Let's say I have multiple buttons that use a class named submit. I add this reference to jQuery.
When the button is clicked; it should be disabled.
Scripts I could use:
A.$('.submit').click(function () { this.disabled = true; });
B.$('#submit').click(function () { this.prop('disabled'), true); });
C.$('#submit').click(function () { $(this).disabled = true; });
D.$('.submit').click(function () { $(this).prop("disabled", true); });
The correct answer is D.
So, personally, I was pretty sure it was C. (which is wrong) because I saw the # before submit. I do this alot with questions like these, where I try to assume I know what the correct selector is. Which I always assume # is the correct one.
In hindsight the question states it's a class so I believe that's where the . is appropriate. However, without being told it's a class, I would have just guessed.
Sometimes I see an id referenced like so #imnotaclass or what have you, which is confusing me.
Can someone better explain jquery selectors to me, when to use # , ., or
In jQuery there's documentation on the selectors. Is there not continuity between other languages ie, css, html. I noticed with those 2 I can pass in something and use # on all fronts. However it will be an id and yet it still works.
The reason I bring this up is to better understand; on an exam what the correct answer is.
If "submit" is a class name, then the correct answer would be A:
$('.submit').click(function () { this.disabled = true; });
because it is the only answer that selects an element with that class name and properly sets the disabled property inside the event handler.
The code inside the event handler is wrong for options B, C and D so there is no way that they could be considered correct.
The question seems to be checking your knowledge of three things: 1) CSS selector syntax, 2) how to set properties on a DOM element and 3) what is the value of this in a jQuery event handler and how you use it.
The two correct ways to set the disabled property inside the event handler are:
// use direct DOM property access
this.disabled = true;
// use jQuery's .prop()
$(this).prop("disabled", true);
As for CSS selector syntax basics:
A selector that starts with # targets an ID value identified with id="xxx" in the HTML or set as a property on the DOM element.
So "#submit" references a single element with an id as in
<button id="submit">Press Here</button>
A selector that starts with . targets a class name identified with class="xxx" in the HTML or set as a property on the DOM element.
So ".submit" references one or more objects with a given class name as in
<button class="submit">Press Here</button>
ID values must be unique in the document (only be used on one single element). Class names can be used on as many elements as desired.
If you want to see more about the selectors that jQuery uses, you can read this tutorial.
If you were going to use jQuery inside the event handler, it would be this:
$('.submit').click(function () {
$(this).prop('disabled', true);
});
As shown in the jQuery documentation for .prop().
If "submit" was an id value, then the correct answer would be:
$('#submit').click(function () {
$(this).prop('disabled', true);
});
Inside your jQuery event handler, the value of this will the DOM element that you registered for the event on. That means if you use something like this.id, then you must be referencing DOM properties or calling DOM methods.
If you want to call jQuery methods, then you would use $(this) to turn it into a jQuery object so you can then use jQuery methods or properties.
Keep in mind that every jQuery object contains an array of zero or more DOM elements. You can access the individual DOM elements in the jQuery object via the array syntax as in $(this)[0] or via the .eq() method as in $(this).eq(0) and a jQuery object has a .length property which tells you how many DOM elements are in the array as in $(this).length === 1.
This is excerpted from the documentation. I'm posting this here as supporting information for #jfriend00's answer
Sizzle is the selector engine which jQuery uses.
Note: In supported browsers, jQuery will attempt to use document.querySelectorAll() to resolve CSS selector queries rather than it's internal selector engine if the requested selector does not use jQuery extensions that are not supported by document.querySelectorAll(). This is done for performance reasons
Selectors
CSS3
Sizzle supports virtually all CSS 3 Selectors, including escaped selectors (.foo\+bar), Unicode selectors, and results returned in document order. The only exceptions are those that would require additional DOM event listeners to keep track of the state of elements.
As such, the following pseudo-selectors are not supported:
:hover
:active
:visited, :link
Note: These CSS3 pseudo-selectors were unsupported prior to version 1.9:
:target
:root
:nth-last-child
:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type
:lang()
Other selectors and conventions
Changes
Full selector lists in :not(); e.g. :not(a.b), :not(div > p), :not(div, p)
Nested pseudo-selectors; e.g. :not(:has(div:first-child))
Additions
[NAME!=VALUE]: Elements whose NAME attribute doesn't match the specified value. Equivalent to :not([NAME=VALUE]).
:contains(TEXT): Elements with textContent containing the word 'TEXT'. Case-sensitive.
:header: Header elements (h1, h2, h3, h4, h5, h6).
:parent: Elements with at least one child node (either text or an element).
:selected: (option) elements that are currently selected
Form Selector Additions
Note: In this context, input, button, select, and textarea are all considered to be input elements.
:input: Input elements
:button: Input elements that are buttons or have type "button"
:checkbox, :file, :image, :password, :radio, :reset, :submit, :text: Input elements with the specified type
Positional Selector Additions
In this context, "positional" refers to an element's placement in the collection after a selection, based on document order. For example, div:first would return an array containing the first div on the page, while div:first em would target the first div on the page and select all em elements within.
Note: Positional indexes begin at zero.
:first/:last: The first/last matching element
:even/:odd: Even/odd-numbered elements
:eq/:nth: The nth element; e.g. :eq(5) finds the 6th element
:lt/:gt: Elements at positions above/below the specified position
https://github.com/jquery/sizzle/wiki#selectors

appending element and removing it destroys all event handlers in jquery?

Ok I create element, assign click handler, and append it to body. Then i remove it and reappend it and click handler is no longer working???
Why would this happen.
var btn = $('<button>').text('hi').click(function(){console.log(3);});
var div = $('<div>');
div.append(btn);
$('body').append(div);
//click it now, it works..
div.html('');
div.append(btn);
// now button doesn't work..
So why is this happening and what can i do to fix it.
Since .html('') is essentially the same as .empty(), the following applies (from the jQuery docs):
To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves.
If you want to remove elements without destroying their data or event handlers (so they can be re-added later), use .detach() instead.
One option would be to use event delegation. In doing so, the event isn't bound directly to the button element, it is bound to a constant parent element that isn't removed.
Example Here
$(document).on('click', 'button', function () {
// ..
});
As mentioned above, another option would be to use the .detach() method in order to remove the element from the DOM, without removing attached event listeners.
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.
Example Here
div.find('button').detach();
div.append(btn);
put this after the second div.append(btn); - > btn = $('button').text('hi').click(function(){console.log(3);});
This is very interesting situation. What happens when you clear div with html('') method. Take a look at source code and you will see that internally jQuery calls jQuery.cleanData(getAll(elem, false));. This method is responsible for removing all the related data for all child elements that have already been removed. This is important in order to avoid memory leaks.
Clearing data also removes events bound with on (and similar) methods, because those event handlers are also stored in internal cache object.
So as the result, even though you removed content of the div, the btn object is still in memory, but the event bound to it previously is gone.
This was the explanation of the problem. The solution is to use dedicated method called detach. It will remove button from the DOM but will keep event data in case element will later be appended again.
// remove element but keep its data
btn.detach();
// append back
div.append(btn);
In situations like this you should not use html('').
It happens because you're calling html() on the DIV that contains the button.
When you call html() with an empty string, it calls empty() internally.
Calling empty() on an element iterates over all the elements inside that element removing all data and events securely.
It does this by calling jQuery.cleanData on the button, which again explicitly calls jQuery.removeEvent, removing all events on the button.
The button is still stored in the variable btn, so it can be appended again, but it has lost all data and any events attached to it because the parent element had html("") called on it.
The solution is to use detach() to remove the element with all the data and events intact, so it can be appended again, or you can attach the event to a parent element that isn't removed, or you could just hide the element, generally there's no reason to remove the element just to reappend it, it's better to hide it.
FIDDLE

Access to object property or method from appended element jquery

I would like to know if it's possible to access an object property from an appended element. For example:
function anyFct(){
this.div=$('<div ref="dv">').html('Hi').appendTo('body');
div.animal='dog';
div.yld=function(){
alert(div.animal);
};
$('input type="text" value="anyIn" onclick="yeldAnimal(this);"').appendTo(div);
}
function yeldAnimal(obj){
var actElement=$(obj).closest('div[ref=dv]');
actElement.yld(); // I want that this yields 'dog'
}
and my HTML:
<input type="button" value="test" onclick="anyFct();">
So this is the logic: I create a div element when the button is clicked on. This div element has a text that when clicked on calls an external function that calls a method on its parent element (the div).
For many contextual reasons this must be the logic. I've already found a solution that is saving the object div in a global array and then search in all values of the array for the object that triggered the method. However, I would like to know if there is a 'cleaner' or correct way to do this.
It's possible, and there are a couple of ways you could achieve it. The important thing you need to understand is the distinction between jQuery objects and actual DOM elements. When you use jQuery to create a <div> element, you create both; but what you end up with a reference to is the jQuery object - or, if you're chaining jQuery function calls, the result of the last function called. The DOM element, assuming you actually append it to the DOM, persists once that section of code has finished execution, but the jQuery object that's created will vanish when that variable goes out of scope.
When you execute some jQuery code later on to get a reference to your DOM element, it's referring to the same element on your page but it's a different jQuery object, so any custom properties you added to the original one won't be available. How do you get around that? Set the properties on the actual DOM element.
You can use the .get() method to access the underlying DOM element from a jQuery object, indexed from 0 (so .get(0) called on a jQuery object will return the first DOM element it references). With that you can then set your custom properties and later retrieve them, something like this:
function anyFct(){
this.div=$('<div ref="dv">').html('Hi').appendTo('body');
var elem = div.get(0); // the actual DOM element, the div
elem.animal='dog';
elem.yld=function(){
alert(elem.animal);
};
$('<input type="text" value="anyIn" onclick="yeldAnimal(this);"/>').appendTo(div);
}
function yeldAnimal(obj){
var actElement=$(obj).closest('div[ref=dv]').get(0); // also the div
actElement.yld(); // alerts 'dog'
}
jsFiddle demo
Note that I've made a few changes to your code in addition to adding in the usage of .get(), most notably correcting the syntax for creating the <input type="text"> element in the first function.
Okay, most of this is not syntactically correct javascript and seems to be overly complicated. I believe if I understand what you're trying to achieve you want the following:
function anyFct(){
var div=$('<div ref="dv">').html('Hi');
div.animal='dog';
div.yld=function(){
alert(this.animal);
};
var element = $('<input type="text" value="anyIn">');
$(element).click(function() {
div.yld();
});
$(div).append(element);
$('body').append(div);
}

add attribute to DOM element

I have a modal form that is generated using bootstrap 3. It doesn't look like there is a reliable way to determine when that form is being shown onscreen. I am attempting to create one. I attached two events to my DOM element that signal when it is shown and when it is hidden.
jq_modal_login_form = $('#modal-login-form')[0]
jq_modal_login_form.on('shown.bs.modal', function () {
jq_modal_login_form.active_onscreen = true;
});
jq_modal_login_form.on('hidden.bs.modal', function () {
jq_modal_login_form.active_onscreen = false;
});
I tried to give an attribute named active_onscreen to the DOM element above. When I look at the DOM element in the debugger later, the attribute is not present.
I should mention that I am VERY new to javascript. Is attribute even the right word to use here? It looks like attribute is a bit of a misnomer as well. It could be an attribute of the object but could also be an attribute of the object.attributes attribute, right? I assume the later is where styling ect., goes and is not what I want to change. Does anyone have some insight as to what I should be doing here?
In jQuery:
$('selector').attr('attribute_name', 'value');
However, you can should only use predefined attributes as creating custom attributes requires additional setup (see this question) that is not necessary in your case.
In your case, you may just want to add a active_onscreen class to the element. Classes are meant to be used to identify elements (and not just for CSS), so they are perfect for this applicaiton. You would use this to add a class to an element:
$('selector').addClass('active_onscreen').
When it is no longer active, you would use this to remove the class:
$('selector').removeClass('active_onscreen').
What you are doing here is adding a property of the DOM object - not an attribute of the element.
Adding an attribute does not necessarily make the property mirror it. Only built-in properties do this.
If you want to set an attribute, but not the property, you can use jQuery's .attr() method.
If you just want to see if a given modal is open, Bootstrap does that for you. You can check the bs.modal data attribute:
$("element").data('bs.modal').isShown;
or a class (but this method is prone to race conditions):
$('#myModal').hasClass('in');

Simple Horizontal Javascript Navigation with Prototype

I'm trying to implement a simple horizontal navigation menu that just shows a single div for each link. It is kinda like a dropdown menu but instead of a mouseover triggering a dropdown, an onclick event will trigger the showing of a div. I want to make sure I am taking the right approach before going too much further, any help is appreciated. This is what I have so far:
<ul id="settings_nav">
<li>
<a>Theme</a>
<div id="settings_block"><%= render :partial => 'email_password' %></div>
</li>
<li>
Lists
<div id="settings_block"><%= render :partial => 'lists' %></div>
</li>
</ul>
window.onload = function(){
settingsMenuInit('settings_nav')
}
function settingsMenuInit(settings_nav){
$(settings_nav).childElements().each(
function(node){
node.onclick= function(){ this.next.show() };
})
}
Something like that, but I am unsure how to get the div that is currently shown and hide it. I could iterate through all the childElements and hide each div and then show the one that is being clicked, but maybe there's a better way?
Some notes FW(T)W:
With Prototype and similar libraries, you don't want to hook up event handlers by assigning functions to the element's onclick and similar properties; that style has several disadvantages (not least that there can only be one handler for the event on the element). Instead, use Prototype's observe function:
someElement.observe('click', functionRefHere);
// or
Element.observe(someElementOrID, 'click', functionRefHere);
This also lets Prototype work around some IE memory loss bugs for you.
You might look at is Prototype's dom:loaded event, which happens sooner than window.onload (which won't happen until all of your images and other external resources have loaded, which can be a second or two after the page is displayed):
document.observe('dom:loaded', initFunctionRefHere);
You can use event delegation and just watch your settings_nav element, rather than each child node individually.
$(settings_nav).observe('click', handleNavClick);
function handleNavClick(event) {
var elm = event.findElement("some CSS selector here");
if (elm) {
event.stop();
// Handle it
}
}
As you can see, Event#findElement accepts a CSS selector. It starts with the actual element that was clicked and tries to match that with the selector; if it matches, it returns the element, otherwise it goes to the parent to see if it matches; etc. So with your HTML you might look for a li (event.findElement('li')) or the link (event.findElement('a')).
But if you want to watch each one individually, they can share a function (as they do in your example):
$(settings_nav).childElements().invoke('observe', 'click', handleNavClick);
function handleNavClick(event) {
// Prototype makes `this` reference the element being observed, so
// `this` will be the `li` element in this example.
}
Whether you watch each element individually or use event delegation depends on what you're doing (and personal preference). Whenever anything is likely to change (adding and removing navigation li elements, for instance) or when there are lots of things to watch, look to event delegation -- it's much easier simpler to deal with changing sets of elements using event delegation and just watching the parent. When dealing with a stable structure of just a few things (as in your example), it may be simpler to just watch the elements individually.
Once inside your handler, you can use Element#down to find child elements (so from the li, you might use li.down('div') to find the div), or Element#next to get to the next sibling element (e.g., going from the link to the div). Either way, once you have a reference to the div, you can use Element#show and Element#hide (or Element#toggle).
I recommend using named functions instead of anonymous ones (see my example above). Named functions help your tools (debuggers, browsers showing errors, etc.) help you. Just be sure not to declare a named function and use it as an expression (e.g., don't immediately assign it to something):
// Don't do this because of browser implementation bugs:
someElement.observe('click', function elementClickHandler(event) {
// ...
});
// Do this instead:
someElement.observe('click', elementClickHandler);
function elementClickHandler(event) {
// ...
}
...because although you should be able to do that according to the spec, in reality various bugs in various browsers make it not work reliably (article).

Categories

Resources