I have some text that I wish to display, or not, based on a test. I have placed the text in a <span /id="..."> block, and have found examples that show referencing the id directly (if...then below) or by using the document.getElementById function (...else below).
Both seem to work in my test case. I gather that using the getElementById function is correct. Is it also correct to reference without calling that function, or is this just a case where it works in this browser now, but may break using a different browser or browser version?
Is there a better method do accomplish this?
<span id="myText">Some text to display or hide</span>
<script type="text/javascript">
function SetVisibility()
{
if (button.checked)
{
myText.style.visibility="visible";
}
else
{
document.getElementById("myText").style.visibility="hidden";
}
}
According to this answer, it is a bad idea to use inline HTML event attributes.
You can also add an event listener by using getElementById then adding addEventListener() to your button element like this:
document.getElementById("myButton").addEventListener("click", function() {
var myText = document.getElementById("myText");
myText.style.visibility = myText.style.visibility === "hidden" ? "visible" : "hidden";
})
<span id="myText">Some text to display or hide</span>
<button id="myButton">SetVisible</button>
It would be best to use eventListener() to set up event handlers.
As mentioned in the comments, the support for this seems pretty widespread -- it started in IE and seems to work across Chrome/Firefox/Safari as of my testing just now. However, using global variables is often considered an anti-pattern outside of exceptional cases where it makes sense -- native web APIs or situations where you need to be able to access something globally, for example. Otherwise you run the risk of overwriting or being overwritten by other code trying to compete for those names. In short -- in this case, it is almost always better to use getElementById, although it is good to be aware that this feature exists.
Related
I have written some code that changes an input quantity on a magento 1.9 ecommerce website.
jQuery("input.qty").val("10");
The problem is the javascript that triggers the total to update doesn't fire. I have found the code responsible and it looks like this:
(function() {
var qtyFields = $('super-product-list').select('input.qty');
qtyFields.each(function(el) {
el.observe("change", updateGroupedPrice);
});
Event.observe(window, "load", updateGroupedPrice);
function updateGroupedPrice() {
//do stuff
}
})();
I think this is using prototype.js but I tried to isolate it in a codepen but couldn't get it working.
I have tried to trigger the change event like so:
jQuery("input.qty").trigger("change")
But it does not work. I also ran through a load of other events but in the dev tools it shows the code listening on "change".
Does anyone know why I can't trigger the change?
Since the page is using Prototype.js, you ought to keep using that to trigger your change. If you introduce jQuery into this, you're a) loading another complete duplicate of what Prototype already does, and b) asking for a lot of trouble isolating the fact that $() is a method in both libraries.
Your jQuery is a little fishy to me, too. You're setting the value of one picker (I imagine) and yet you are addressing it with a classname, so potentially there is more than one select.qty in the page, and all of them will change to value 10, firing off (potentially) multiple callback functions.
The Prototype code you see here is setting up a "listener" for changes on what you would address in jQuery as$(#super-product-list input.qty) inputs.
jQuery always treats $() as returning an array of objects, and thus all of its methods act on the array, even if it only contains one member. Prototype has two different methods for accessing elements in the DOM: $('some_id'), which always returns one element (or none, if no match), and $$('some css selector'), which always returns an array (of zero or more matching elements). You would write (or use native) callback methods differently, depending on which accessor you used to gather the element(s).
If you want to change one of these inputs, you will need to isolate it before you set its value.
Let's say there are three select pickers with the classname qty in your #super-product-list element. You want to change the third one to 10:
$('super-product-list').select('input.qty').last().setValue('10');
Or, much smarter than this, you add an ID to the third one, and then your code is much shorter:
$('quantity_3').setValue('10');
In either case, this will send the "change" event from your select, and the updateGroupedPrice method will observe that and do whatever you have coded it to do.
You won't need to (and should not ever) trigger the change event -- that's a "native" event, and the browser owns it. jQuery's trigger() (which is fire() in Prototype, is used exclusively for "synthetic events", like you see in Bootstrap: show.bs.modal, hide.bs.modal, etc. You can spot these by the punctuation in their names; usually dots or colons to namespace the events and avoid collisions with other code.
Finally, if you really, really, really wanted to change every single #super-product-list select.qty element on the whole page to '10', you would do this in Prototype.js:
$$('#super-product-list select.qty').invoke('setValue', 10);
I'm trying to write a Firefox extension that adds elements to the loaded page. So far, I get the root element of the document via
var domBody = content.document.getElementsByTagName("BODY").item(0);
and create the new elements via
var newDiv = content.document.createElement("div");
and everything worked quite well, actually. But the problems came when I added a button with on onclick attribute. While the button is correctly displayed, I get an error. I already asked asked here, and the answer with document.createElement() (without content) works.
But if I remove the 'content.' everywhere, the real trouble starts. Firstly, domBody is null/undefined, no matter how I try to access it, e.g. document.body (And actually I add all elements _after_the document is fully loaded. At least I think so). And secondly, all other elements look differently. It's seem the style information, e.g., element.style.width="300px" are no longer considered.
In short, with 'content.document' everything looks good, but the button.onclick throws an error. with only 'document' the button works, but the elements are no longer correctly displayed. Does anybody see a solution for that.
It should work fine if you use addEventListener [MDN] (at least this is what I used). I read somewhere (I will search for it) that you cannot attach event listener via properties when creating elements in chrome code.
You still should use content.document.createElement though:
Page = function(...) {
...
};
Page.prototype = {
...
addButton : function() {
var b = content.document.createElement('button');
b.addEventListener('click', function() {
alert('OnClick');
}, false);
},
...
};
I would store a reference to content.document somewhere btw.
The existing answer doesn't have a real explanation and there are too many comments already, so I'll add another answer. When you access the content document then you are not accessing it directly - for security reasons you access it through a wrapper that exposes only actual DOM methods/properties and hides anything that the page's JavaScript might have added. This has the side-effect that properties like onclick won't work (this is actually the first point in the list of limitations of XPCNativeWrapper). You should use addEventListener instead. This has the additional advantage that more than one event listener can coexist, e.g. the web page won't remove your event listener by setting onclick itself.
Side-note: your script executes in the browser window, so document is the XUL document containing the browser's user interface. There is no <body> element because XUL documents don't have one. And adding a button won't affect the page in the selected tab, only mess up the browser's user interface. The global variable content refers to the window object of the currently selected tab so that's your entry point if you want to work with it.
Code: http://dpaste.org/Oerz/
The page looks as it should, with the title, 5 paragraphs and buttons all appearing. Each button is supposed to make a small alteration to a specific elements styling.
Currently no action occurs when any of the buttons are clicked.
I'm only claiming the above code is error free based on finding no errors in chrome's javascript console or firebug. Then again, I'm new to all this so I don't know if I'm using them correctly.
Any help would be appreciated.
If your going to use jQuery then bind to click events like this
$('#oneButton').click(boldTheText);
or
$('#oneButton').bind("click", boldTheText);
Reference
http://api.jquery.com/click/
http://api.jquery.com/bind
You're assigning to onClick when you add the event handlers to your buttons. Assign to onclick instead -- note the lowercase "C".
There are no errors in the console because it's perfectly valid to assign something to an object's onClick property; the browser just won't ever use it.
You're really not utilizing the potential of jQuery here... I'm not exactly sure why your code doesn't work, but instead of looking into that specifically, I'll give you some hints on how to improve it. Most likely, it will start working in the process =)
For one thing, you never need document.getElementById() anymore. jQuery's selectors are way more powerful, and to select a button with id="oneButton" you simply use $('#oneButton'). Most of them looks and works like css selectors, so you already know them.
Secondly, you don't have to define all your methods with names to hook them up - instead, use anonymous functions (and hook them up using jQuery's click or live):
$('#oneButton').click(function() {
// Assuming you've defined .bold{font-weight:bold;} in your stylesheet
$('.firstDiv').addClass('bold');
// Assuming you haven't
$('.firstDiv').css('font-weight','bold');
// You only need one of the two above!
});
Thus, the first two buttons could be hooked up to do exactly the same things using only these few lines of code:
$('#oneButton').click(function() {
$('div.firstDiv').css('font-weight', 'bold');
});
$('#twoButton').click(function() {
alert('There are ' + $('p').size() + ' paragraphs.');
});
Edit: As Mark Bridges noted in a comment to another answer, changing onClick to onclick (with a lowercase c) solves your immediate problem. I wouldn't be satisfied with that though, since you have jQuery at your disposal.
I've simplified your code a little bit here to better take advantage of jquery's syntax:
<title>jQuery Example</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
window.onload = function() {
$('#one').click(function() {
$('#first').css('font-weight', 'bold');
});
};
</script>
</head>
<body>
<div class="firstDiv">
<p id="first">I should be bold.</p>
</div>
<button type="button" id="one">Bold</button>
</body>
</html>
Most importantly:
Use of $('#foo') instead of document.getElementById('foo') returns you jQuery objects with richer interaction semantics.
Use of the .click() method on a jquery object to define a click handler.
Use of the .css() method on a jquery object to make style changes.
How can I find out which FORM an HTML element is contained within, using a simple/small bit of JavaScript? In the example below, if I have already got hold of the SPAN called 'message', how can I easily get to the FORM element?
<form name="whatever">
<div>
<span id="message"></span>
</div>
</form>
The SPAN might be nested within other tables or DIVs, but it seems too long-winded to iterate around .parentElement and work my way up the tree. Is there a simpler and shorter way?
If it wasn't a SPAN, but an INPUT element, would that be easier? Do they have a property which points back to the containing FORM? Google says no...
The form a form element belongs to can be accessed through element.form.
When the element you are using as reference is not a form element, you'd still have to iterate through the parentElement or use some other kind of selector.
Using prototype, you could simplify this by using Element.up():
$(element).up('form');
Other answers to this question have pointed out how to do the same in jQuery.
Why not just use:
var nodesForm = node.form;
?
It works on FF3, FF4, google chrome, Opera, IE9 (I tested myself)
Guess you have to iterate through all elements then.
You can try using jQuery:
$("input").parent("form")
http://docs.jquery.com/Traversing/parent#expr
Regarding Gumbo's Post:
As much as prototype and jQuery are useful, some people don't implement them into their projects.
Could someone please explain why Gumbo's solution was downgraded, other than the fact that he repeated what the OP was originally trying to avoid?
node = document.getElementById(this.id);
while (node.nodeName != "FORM" && node.parentNode) {
node = node.parentNode;
}
To answer the OP's question:
Traversing the DOM is the fastest way to achieve this effect - perceived speed is accomplished by 1) better written JS code, or 2) execution time (if you store the form on page load for that element, you'll still be traversing, but you'll have a quicker call to a stored variable when you need to retrieve that information).
There are no attributes nested in non-form elements that would associate it with the form (span.form does not exist).
If you are using a script (php/perl) to generate your page, and you're going to be making a lot of calls to the form, you could embed the form id in the HTML for that element. Still, a look up would need to occur.
I hope this helps,
vol7ron
You could backtrack in the DOM tree until you get to the right node:
node = document.getElementById("message");
while (node.nodeName != "FORM" && node.parentNode) {
node = node.parentNode;
}
Or a small jQuery (ignoring jQuery itself):
$("#message").parents("form:first")
Other than node.parentNode, I don't believe there is a way to find a specific ancestor of a given node. Most libraries usually do what you describe and iterate up through parentNode.
If you're not using a library like prototype, jquery or Ext, it would probably be a good idea. By now, they've resolved all the incompatibilities and quirks in the DOM to make most operations like this a trifle.
Can I add a custom JavaScript as some DOM node attribute to perform when it's ready like JavaScript added as "onClick" attribute performed when you click on it?
Suppose i need to process a div element with some funcMyTransform function.
I think it'll be more elegant to write smth like this
<div onReady=funcMyTransform(this)>...</div>
Instead of
<div id="MyElement">...</div>
<script type="text/javascript">
$(document).ready(function(){$("#MyElement").funcMyTransform()});
</script>
Is there such "onReady" attribute or something?
There is no onReady event. Depending on the functionality, you may want to abstract funcMyTransform out to a jQuery plugin. E.g.
jQuery.fn.funcMyTransform = function() {
alert(this.id);
};
jQuery(document).ready(function(){
jQuery("#MyElement").funcMyTransform(); // Alerts => "MyElement"
});
If you want access to the element as soon as its "ready", then placing your script at the bottom the document (before </body>) would probably be a better idea, and is definitely faster (albeit slightly) to initiate than jQuery's pseudo "ready" event.
As others have said, onWhatever attributes are "like so 1999" ;-). The general consensus among the modern Javascript community is that you should avoid using them as much as possible (for maintainability and other reasons).
That being said, there is a way to get something very similar to what you want in a much more maintainable fashion:
<div class="onReadyDoMyTransform">...</div>
<script type="text/javascript">
$(function(){$(".onReadyDoMyTransform").funcMyTransform()});
// $(someFunction) == $(document).ready(someFunction);
</script>
This approach will give you all the benefits of being able to decide what "transforms onReady" in your HTML layer, but without all the failings of an "onReady" attribute. The script part can just go in to a common JS include that you use throughout your site, so you don't have to worry about adding it along with every DIV.onReadyDoMyTransform.
Have you tried the jQuery ready?
$("#MyElement").ready(function(){$(this).funcMyTransform()});
I'm guessing this won't work but worth a shot?
Your suggestion that mixing code with the HTML as ... more elegant is arguable.
<div onReady=funcMyTransform(this)>...</div>
In general this is discouraged as it leads to maintenance problems. If I understood you correctly, on the onReady event being activated you want to call all these functions. One way to do it actually is to use a REL or anything else you might want to use as an expando attribute.
On document ready capture all the elements and read the attribute's value, then eval(). (People will tell you eval() is evil, but is quite harmless here).
$(document).ready(function(){
var v= $('.class_name').attr('rel');
eval(v);
})
It will actually eval() and activate all your javascript within your REL attributes.
I have used er (element-ready) by Stuart Colville before that is somewhat similar to what you are talking about, except for it's not an element attribute.
http://muffinresearch.co.uk/archives/2006/04/12/element-ready/
I have used this in an instance where I was returning some html via an XHR request and I wanted it to execute some javascript, but the dom had to be finished loading before I could execute it. Of course document ready didn't work, since the main document was already loaded.