Generally, there are 3 ways (that I am aware of) to execute java开发者_运维问答script from an <a/>
tag:
1) Use onclick()
:
<a href="#" onclick="alert('hello'); return false">hello</a>
2) Directly link:
<a href="javascript:alert('hello')">hello</a>
3) Or attach externally:
// In an onload event or similar
document.getElementById('hello').onclick = window.alert('Hello');
return false;
<a id="hello" href="#">hello</a>
I am actually loading the link via AJAX, so #3 is basically out. So, is it better to do #1 or #2 or something completely different? Also, why? What are the pitfalls that I should be aware of?
Also of note, the anchor really doesn't link anywhere, hence the href="#"
, I am using a
so the styles conform as this is still an object to be clicked and a button is inappropriate in the context.
Thanks
If you are loading the content via ajax and need to hook up event handlers, then you have these choices:
- Put a javascript handler in your HTML with your option 1) or 2). In my mind option 1) is a cleaner way of specifying it, but I don't think there's a mountain of difference between 1) or 2) - they both do essentially the same thing. I'm not a fan of this option in general because I think there's value in keeping the markup and the code separate.
- After loading the content with ajax, call some local code that will find and hook up all the links. This would be the same kind of code you would have in your page and execute on DOMReady if the HTML had been static HTML in your page. I would use addEventListener (falling back to attachEvent) to hook up this way as it more cleanly allows multiple listeners for a single object.
- Call some code after you load the content with ajax that finds all the links and hooks up the clicks to some generic click handler that can then examine meta data in the link and figure out what should be done on that click based on the meta data. For example, this meta data could be attributes on the clicked link.
- When you load the content, also load code that can find each link individually and hook up an appropriate event handler for each link much the way one would do it if the content was just being loaded in a regular page. This would meet the desire of separating HTML from JS as the JS would find each appropriate link and hook up an event handler for it with addEventListener or attachEvent.
- Much like jQuery
.live()
works, hook up a generic event handler for unhandled clicks on links at the document level and dispatch each click based on some meta data in the link. - Run some code that uses an actual framework like jQuery's
.live()
capability rather than building your own capability.
Which I would use would depend a little on the circumstances.
First of all, of your three options for attaching an event handler, I'd use a new option #4. I'd use addEventListener (falling back to attachEvent for old versions of IE) rather than assigning to onclick because this more cleanly allows for multiple listeners on an item. If it were me, I'd be using a framework (jQuery or YUI) that makes the cross browser compatibility invisible. This allows complete separation of HTML and JS (no JS inline with the HTML) which I think is desirable in any project involving more than one person and just seems cleaner to me..
Then, it's just a question for me for which of the options above I'd use to run the code that hooks up these event listeners.
If there were a lot of different snippets of HTML that I was dynamically loading and it would be cleaner if they were all "standalone" and separately maintainable, then I would want to load both HTML and relevant code at the same time so have the newly loaded code handle hooking up to it's appropriate links.
If a generic standalone system wasn't really required because there were only a few snippets to be loaded and the code to handle them could be pre-included in the page, then I'd probably just make a function call after the HTML snippet was loaded via ajax to have the javascript hook up to the links in the snippet that had just been loaded. This would maintain the complete separation between HTML and JS, but be pretty easy to implement. You could put some sort of key object in each snippet that would identify which piece of JS to call or could be used as a parameter to pass to the JS or the JS could just examine the snippet to see which objects were available and hook up to whichever ones were present.
Modern browsers support a Content Security Policy or CSP. This is the highest level of web security and strongly recommended if you can apply it because it completely blocks all XSS attacks.
The way that CSP does this is disabling all the vectors where a user could inject Javascript into a page - in your question that is both options 1 and 2 (especially 1).
For this reason best practice is always option 3, as any other option will break if CSP is enabled.
Number 3 is not "out" if you want to load via AJAX.
var link = document.createElement("a");
//Add attributes (href, text, etc...)
link.onclick = function () { //This has to be a function, not a string
//Handle the click
return false; //to prevent following the link
};
parent.appendChild(link); //Add it to the DOM
I'm a firm believer of separating javascript from markup. There should be a distinct difference, IMHO, between what is for display purposes and what is for execution purposes. With that said, avoid using onclick
attribute and embedding javascript:*
in a href
attribute.
Alternatives?
- You can include javascript library files using AJAX.
- You can setup javascript to look for changes in the DOM (i.e. if it's a "standard task", make the anchor use a CSS class name that can be used to bind a specific mechanism when it's later added dynamically. (jQuery does a great job at this with
.delegate()
)) - Run your scripts POST-AJAX call. (Bring in the new content, then use javascript to [re]bind the functionality) e.g.:
function ajaxCallback(content){
// add content to dom
// search within newly added content for elements that need binding
}
Other answers have already addressed the aspects of security and of separating markup and code, but I think there is more to say about the differences between specifying the javascript function in the href
attribute on the one hand (option 2) and attaching it as an event-handler on the other hand (options 1 or 3 or using addEventListener
). And knowing these differences will certainly help choosing the approach depending on the use case and on personal preference.
different session history
Clicking an anchor tag with a specified href
attribute usually adds an entry to the browser session history. However, if href
is of type "javascript:..."
(option 2) this does not seem to be the case. The appearance of the link remains "unvisited" after clicking (I haven't found this anywhere in the specs, but it is the current behavior of Chrome 110 and Firefox 110) and using browser-back goes to whatever page was in the history already before clicking. This might be wanted behavior, depending on the use case.
If instead href is of type "#"
(options 1 and 3), this is a valid relative url and adds an entry to the browser history. After clicking it, it will appear "visited" and the browser history has an additional entry. As mentioned in the question, the reason for using "#"
is that without any href
attribute, the browser will not style the element as a link. And relative hashtag links or URI fragments do not induce a request to the server, but are cross-links within the webpage only (pointing to an html-element with its id specified in the fragment or - if the fragment is empty like here - to the complete document). But if one uses the same href="#"
for several javascript-links, then one should take into account that all of them will appear as "visited" links after clicking only one of them. So one might want to use instead unique hashtags for each of them. Depending on the use case this can be made consistent with the true meaning of the URI-fragment. E.g. if the javascript function draws a chart within a certain html-element, one could use the id of this element, so that the browser will scroll there. One can also use hashtags that simply don't exist as id's on the page (so no scrolling), but this is a slight abuse of the URI fragment. The additional history-entry contains only the URL (with different fragment), but no information about the javascript-call. One can use the history-API (e.g. history.replaceState
- see MDN) to add more information to the history-entry, such that (if wanted) the full state of the web-page after the javascript-call could also be restored when browsing the history to this entry. In contrast, for option 2, in order to achieve the same, one would need to use history.pushState
(see MDN ) instead of replaceState
(as there is no history entry added automatically in this case)
event accessibility
An additional difference between the options of the question is, that when using event handlers, one has access to the event and can e.g. check which mouse-button was used for the click or if some additional key was pressed (shiftKey, ctrlKey, altKey, metaKey). This is not possible with the approach 2, or only if one adds an additional click handler which stores the information and makes it accessible for the function that is called in the href
attribute ...
精彩评论