I am trying to overwrite a JavaScript on change event in the below code with jQuery however I believe that the inline JavaScript is taking priority over the jQuery functionality declared. Essentially I am trying to have an AJAX version of my site which includes an additional JavaScript file. I need this functionality to still work without the additional AJAX version, but I am not sure as to whether I should include it in the main JavaScript file or leave it inline like it is right now. Any suggestions and information regarding them would be greatly appreciated! Thanks!
<form action="/cityhall/villages/" method="post" name="submit_village">
<select name="village" onchange="document.submit_village.submit();">
<option value=""></option>
</select>
</form>
I am trying to use the jQuery Form Plugin to submit the posts to the PHP to handle the requests as follows:
var bank_load_options = {
target: '#content',
beforeSubmit: showRequest,
success: showResponse
};
$('form.get_pages').livequery(function(){
$(this).ajaxForm(bank_load_options);
return false;
});
I modified the code as following:
<form action="/cityhall/villages/" method="post" id="submit_village" name="submit_village">
<select name="village" class="get_pages" rel="submit_village">
<option value=""></option>
</select>
</form>
<script>
# main JavaScript
$('.get_pages').live('change',function(e){
var submit_page = $(this).attr('rel');
$("#"+submit_page).submit();
});
# ajax JavaScript
var bank_load_options = {
target: '#content',
beforeSubmit: showRequest,
success: showResponse
};
$('.get_pages').live('change',function(){
var submit_page = $(this).attr('rel');
$("#"+submit_page).ajaxForm(get_pages_load_options);
return false;
});
</script>
However now it only runs every other option when I change it.
Does your non-ajax version also have to be non-jQuery? If not then I also suggest (as #nnnnnn did) that you do away with the onxyz attributes and use jQuery throughout.
As other posters have suggested, inline events are inadvisable and should be done away with.
If your non-ajax version has to use onxyz then I'd suggest the following:
Setup a variable that will store whether this version is to be ajax or not (how you determine this is down to you and your app context):
var is_ajax = true;
Then if it is an ajax version try this:
$(function(){
if(is_ajax){
//Unbind the change event from the village select box
$('select[name="village"]').unbind('change');
//As an extra measure, remove the onchange attribute
$('select[name="village"]').removeAttr('onchange');
//Re-bind the click event to execute your new ajax functionality
$(document).on('change','select[name="village"]',function(){
//Do ajax stuff for on-change event here
});
}
});
Because it sits in the document.ready function of jQuery this code should fire after the html dom (and it's associated onchange attributes) have been rendered. Therefore I'd imagine it'd effectively strip off the old event and re-bind a new one.
Demo:
http://jsfiddle.net/foxwisp/hdL5R/
If you can do away with the inline events then I imagine you'd want to do this in your initial javascript file:
$(document).on('change','select[name="village"]',function(){
document.submit_village.submit();
});
And then the following in your ajax file to be included later:
$('select[name="village"]').removeAttr('onchange');
$(document).on('change','select[name="village"]',function(){
//Do ajax stuff here
});
As long as both are in the same scope and timeframe such that they're executed one after the other then the above should work.
It is good practice to have all your Javascript in one place (not one file specifically, but not half inline, half in a file), as such I would remove all inline Javascript.
This would also your help with priority issue as you can manage it all from the same location.
Where you put your javascript is (largely) a choice of style. Much like CSS, putting javascript in the page source (inline) or in the page head is good practice for writing your page initially but bad practice for production pages.
That being said, I am a bit confused as to why you would double up your onchange events. Are you trying to use jQuery for the select element too?
I am not sure what the general behavior is for inline vs separate javascript, but I would venture to say that, similar to inline CSS, the in-page javascript will take precedence over an external file.
In this particular case, you could, in your AJAX version, forego the submit that has been triggered.
$('[name=submit_village]').on('submit', function(e) {
e.preventDefault();
//...Maybe other AJAX code...
});
Related
One ajax request in my code returns some html within which certain elements need custom event handling, like this:
<div>
<!--some html-->
<button id='specialbutton'></button>
</div>
<script type='text/javascript'>
$("#specialbutton").click(function(e){
//some special handing of html above
});
</script>
I understand javascript returned from ajax request are not executed automatically, and using eval() to execute return js is considered insecure and generally bad practice. But the code I need to execute here is just local to the html snippet returned (in a Django template), including it in the boilerplate html that sends ajax request seems to violate the rule of seperation. Should I add attribute like 'data-role="specialbutton"' to the element, and globally use jQuery.live() to attach handlers, or what?
What is the best practice here? Any good advice?
You could use some pub/subbing so that the event is only triggered when there is a successful ajax request like so:
$.ajax({
success: function( data ) {
$(document).trigger('some/special/event');
}
})
$(document).on( 'some/special/event', function() {
$("#specialbutton").on( 'click', function(e){
//some special handing of html above
});
});
This will allow you to maintain separation of code and only add the event listener if the the HTML is on the page.
Here is a link to a great post about using custom jQuery events: http://www.sitepoint.com/jquery-custom-events/
I'm trying to do some simple jQuery stuff 'dynamically' from within a MediaWiki content page. Its really just to 'beauty up' some different features.
I've done the following:
http://www.mediawiki.org/wiki/JQuery
http://www.mediawiki.org/wiki/Manual:$wgRawHtml (mainly for Paypal buttons initially)
The below code does not work. This is put in a blank content page.
<html>
<script>
$j(document).ready(function(){
$j('#test').hover(
function(){
$j('#test').attr('background-color','red');
},
function(){
$j('#test').removeAttr('background-color');
}
);
});
</script>
<div id="test">Howdy</div>
</html>
Nothing happens...
Any ideas?
Update:
I have attempted this simple solution with no result.
example.com/wiki/index.php?title=MediaWiki:Common.js
$j('#jTest-Howdy').hover(
function(){
$j('#jTest-Howdy').addClass('jTest-red');
},
function(){
$j('#jTest-Howdy').removeClass('jTest-red');
}
);
example.com/wiki/index.php?title=MediaWiki:Common.css
.jTest-red { background-color: red; }
example.com/wiki/index.php?title=jQueryTest
<html>
<div id="jTest-Howdy">Howdy</div>
</html>
as you can see here, this code should work IF jQuery was being loaded properly...
http://jsfiddle.net/5qFhv/
but it is not working for me... any help?
If you're using the jQuery that's loaded by MediaWiki 1.17, be aware that most JavaScript is loaded after page content. An inline <script> element is executed immediately when it's reached, so $j would not be defined at this time -- you'll probably see some errors in your JavaScript error console to this effect.
(Offhand I'm not sure about the jQuery that's included with 1.16; versions of MediaWiki prior to that as far as I know did not include jQuery.)
Generally what you want to do here is to either put JavaScript code modules into the 'MediaWiki:Common.js' page and let that hook up to your HTML markup, or create a MediaWiki extension -- which you can then invoke from your pages, and which will let you create any fun HTML and JavaScript output you like.
http://www.mediawiki.org/wiki/Manual:Interface/JavaScript
http://www.mediawiki.org/wiki/Manual:Developing_extensions
Code you put in your 'MediaWiki:Common.js' page will be loaded after other UI initialization, ensuring that code and variables are present so you can call into jQuery etc.
I don't know much about MediaWiki, but to me it looks like some simple javascript mistakes.
In the first sample you are trying to set an attribute on the element,
when you need to set the css or style attribute.
$j('#test').css('background-color', 'red');
In both samples you are binding an event to an element that doesn't exist yet in the DOM, so it will fail. You could use the live method, which will work for existing and future elements introduced in the DOM.
$j.('#test').live('mouseover', function(){
$j(this).addClass('hover-class');
}).live('mouseout', function(){
$j(this).removeClass('hover-class');
});
Hope that helps.
Try putting all your custom jQuery code in its own file, then load it as a module with ResourceLoader, after jQuery.
http://www.mediawiki.org/wiki/ResourceLoader/Migration_guide_for_extension_developers
Also, as a debugging method: completely load your site in Firefox, then enter your custom jQuery code in the console. If it works, your problem is a race condition. If it doesn't, jQuery isn't loading for some reason.
What is the best practice of activating jquery ui widgets for html loaded and inserted into the document by ajax?
I am an advocate of unobtrusive javascript and strongly believe that all functionality accessed by javascript should be also accessible without it. So in the ideal case, each form which opens in a popup, should also have its separate page and links to them should be replaced with javascript-based ajax loading.
I find this pattern very useful for loading and inserting a part of another page into the current document:
$('#placeholder').load('/some/path/ #content>*');
Or to make it more generic:
$('a.load').each(function() {
$(this).load($(this).attr('href') + ' #content>*');
});
However, I would also like to activate the javascripts from the dynamically loaded page, so that different widgets function correctly.
I know, I could add those javascripts to the current document and activate all of them in the callback of .load(), or I could use $.get() to get some JSON with html and javascripts separately, but I guess, there might be a more elegant generic solution to this.
What would you recommend?
BTW, I am using Django in the backend.
The question is how you're activating your javascript currently. If you're doing something like:
$(document).ready(function() {
$('a.foo').click(function() { ... });
})
You could consider changin things to:
$(document).ready(function() {
$('a.foo').live('click', function() { ... });
})
That way when new DOM objects are loaded the event handlers are attached.
What I've done is used the "load" option that is specifiable by jquery.ui widgets. Unfortunately, this isn't well documented, so you won't see the option here: http://jqueryui.com/demos/tabs/#options for example, but you will see it here: http://jqueryui.com/demos/tabs/#method-load
For the most part, each of the methods you invoke have an initial option that can be set, which is what prompted me to try using the load.
In my own application, I have 3 levels of nested tabs that are being created dynamically via AJAX. In order to have the javascript for each of the tabs applied dynamically, I have nested load functions that are first initiated when the document is loaded.
So my template file has:
<script type="text/javascript" src="{{ MEDIA_URL }}js/tabs.js"></script>
<script type="text/javascript">
$(function() {
$('.overall_tabs').tabs({
load: initializeOverallTabs
});
});
</script>
My tabs.js file has:
function initializeOverallTabs(event, ui){
...
$('.lvl_two_tabs').tabs({
load: initializeTabLevel2
});
...
}
function initializeTabLevel2(event, ui){
...
// So on and so forth
...
}
Also, I recommend when working inside the loaded areas to make your references be specific to that pane. This was extremely important when working with tabs. The best way I found to do this is below.
In your tabs.js file:
function initializeOverallTabs(event, ui){
$panel = $(ui.panel);
$panel.find('lvl_two_tabs').tabs(...);
}
I found this question strangely coincidental! I recently explained my solution to a few developers to the same situation with the same Jquery/Django Environment. I hope that helped!
One way I decided myself for handling widgets from external pages is parsing the HTML of the other page, searching for scripts and executing them in the current page.
For example, there is a form with autocomplete widget on another page which is loaded and inserted to this page. The autocomplete widget should be activated with specific properties, like available choices:
<script type="text/javascript">
//<![CDATA[
$(function() {
$("#colors").autocomplete({
source: ['red', 'green', 'blue', 'magenta', 'yellow', 'cyan']
});
});
//]]>
</script>
Then in the current page I can have the following script which loads HTML and additionally collects all javascripts within it and executes them:
var oRe = /<script\b[^>]*>([\s\S]*?)<\/script>/gm;
$('#placeholder').load(
'/some/path/ #content>*',
function(responseText, textStatus, XMLHttpRequest) { // <-- callback function
var sScripts = "";
responseText.replace(
oRe,
function($0, $1) {
sScripts += $1;
return $0;
}
);
eval(sScripts);
}
);
One drawback here is that the current document should initially be loading all the libraries which might appear in the included forms. For example, in this case, it would be the jquery-ui including the autocomplete widget. I guess I could extend the code by searching for script tags which load external scripts and loading them in the current document if they are not present.
I'm appending some code to my page using jQuery AJAX calls. This code is a mix of html and javascript. But I want javascript to be executed only when html part is ready. But what event is raised when appended html is rendered?
Here is an example:
<table id="sampleTable">
...
</table>
<script>
// this code should be executed only when sampleTable is rendered
$('#sampleTable').hide();
</script>
Use the jQuery ready() event:
$(document).ready(function() {
$('#sampleTable').hide();
}
<edit> It seems to be impossible to call a ready event on any other object than Document, my bad </edit>
This is an option if you are talking about the event triggered after a successful Ajax request :
$('#sampleTable').ajaxComplete(function() {
$(this).hide();
});
Or just hardcode the style of the table to display:none;...
Wrap your javascript in a "ready" handler. This will fire when the DOM has been updated and is undoubtedly what you want to do.
$(function() {
$('#sampleTable').hide();
});
$(document).ready(function() {
$('#sampleTable').hide();
});
http://api.jquery.com/ready/
You're loading both the <table> and the <script> into the document at once? Don't.
Inserting HTML with <script> elements into the page using html()/load() is highly unreliable. The script doesn't run directly from innerHTML inclusion; different browsers treat such inserted script elements differently. jQuery attempts to fix some of this but doesn't quite get it quite right (it may not be possible to get it quite right).
It's best to keep your static code in static script, so that the caller knows it has to call hide() on the table just after the AJAX call completes and the content is inserted. If you really have to pass back dynamic code to run, keep it separate from the HTML, eg. by returning a JSON object with both HTML and code members.
I have a couple of, what may end up being for this forum, overly-novice questions regarding unobtrusive event handling.
As I understand it, a properly set-up document would look something like this:
<html>
<head>
<title>Title</title>
<script src="jsfile.js" type="text/javascript></script>
</head>
<body>
//Body content, like some form elements in my case
</body>
</html>
Jsfile.js would look something like this:
function a() {
//code;
}
function b()...
window.addEventListener('load', a, false);
document.getElementById("id").addEventListener('click', b, false);
document.myForm.typeSel.addEventListener('change', c, false);
//or to use better browser-compatible code...
function addEvent(obj,evt,fn) {
if (obj.addEventListener)
obj.addEventListener(evt,fn,false);
else if (obj.attachEvent)
obj.attachEvent('on'+evt,fn);
}
addEvent(window, 'load', a);
addEvent(document.getElementById('id'), 'click', b);
addEvent(document.myForm.typeSel, 'change', c);
As I understand it, while in the head the browser will load this JavaScript code, adding each of those event handlers to their respective elements. HOWEVER... While the window handler is added properly, none of the others are. But if placed within a function, the (for instance) getElementById method of accessing an element works just fine, and the event handler is added. So I could conceivably make a loadEvents() function which is called via window onload, which contains all of the addEvent() functions for the other document elements for which I need event handlers. But as I understand the whole thing, I shouldn't have to do this.
In addition, if I were to stick the addEvent code within the body along with the element it addresses, such as:
<input type="checkbox" id="test" />
<script type="text/javascript>
document.getElementById("test").onclick = func;
</script>
...then it works fine. But of course it also violates the whole reason for removing inline event handlers!
So question being: In order to use *element*.addEventListener('click',func,false), addEvent(*element*,'click',func), or even *element*.onclick = func - how can I successfully reference an element at the end of a script file in the head, without having to stick it in another function? Why does getElementById and other such methods not work outside of a function in the head?
Or, is there some flaw in my underlying understanding?
Putting <script> in the <head> used to be the wisdom. But nowadays, with heavy ajax pages, <script> is more and more often but in the body, as far down below as possible. The idea is that the loading and parsing of the <script> keeps the rest of the page from loading, so the user will be looking at a blank page. By making sure the body is loaded as fast as possible, you give the user something to look at. See YAHOO best practices for a great explanation on that issue: http://developer.yahoo.com/performance/rules.html
Now, regardless of that issue, the code as you set it up now, can't work - at least, not when the elements you attempt to attach the handlers to aren't created yet. For example, in this line:
document.getElementById("id").addEventListener('click', b, false);
you will get a runtime error if the element with id="id" is inside the body. Now, if you put the <script> in the body, way below, after the content (including the lement with id="id", it will just work, since the script is executed after the html code for those elements is parsed and added to the DOM.
If you do want to have the script in the head, then you can do so, but you'll need to synchronize the adding of the event handlers with the rendering of the page content. You could do this by adding them all inside the document or window load handler. So, if you'd write:
//cross browser add event handler
function addEventHandler(obj,evt,fn) {
if (obj.addEventListener) {
obj.addEventListener(evt,fn,false);
} else if (obj.attachEvent) {
obj.attachEvent('on'+evt,fn);
}
}
addEventHandler(document, 'load', function(){
//set up all handlers after loading the document
addEventHandler(document.getElementById('id'), 'click', b);
addEventHandler(document.myForm.typeSel, 'change', c);
});
it does work.
The reason why window.addEventListener works while document.getEle...().addEventListener does not is simple: window object exists when you're executing that code while element with id="abc" is still not loaded.
When your browser downloads page's sources the source code is parsed and executed as soon as possible. So if you place script in head element - on the very beginning of the source - it's executed before some <div id="abc">...</div> is even downloaded.
So I think now you know why
<div id="test">Blah</div>
<script type="text/javascript">document.getElementById("test").style.color = "red";</script>
works, while this:
<script type="text/javascript">document.getElementById("test").style.color = "red";</script>
<div id="test">Blah</div>
doesn't.
You can handle that problem in many ways. The most popular are:
putting scripts at the end of document (right before </body>)
using events to delay execution of scripts
The first way should be clear right now, but personally I prefer last one (even if it's a little bit worse).
So how to deal with events? When browser finally download and parse whole source the DOMContentLoaded event is executed. This event means that the source is ready, and you can manipulate DOM using JavaScript.
window.addEventListener("DOMContentLoaded", function() {
//here you can safely use document.getElementById("...") etc.
}, false);
Unfortunately not every browser support DOMContentLoaded event, but as always... Google is the anwser. But it's not the end of bad news. As you noticed addEventListener isn't well supported by IE. Well... this browser really makes life difficult and you'll have to hack one more thing... Yes... once again - Google. But it's IE so it's not all. Normal browsers (like Opera or Firefox) supports W3C Event Model while IE supports its own - so once again - Google for cross-browser solution.
addEventListener might seems now the worst way to attach events but in fact it's the best one. It let you easly add or remove many listeners for single event on single element.
PS. I noticed that you consider of using Load event to execute your scripts. Don't do that. Load event is execute too late. You have to wait till every image or file is loaded. You should use DOMContentLoaded event. ;)
EDIT:
I've forgotten... dealing with cross-browser event model is much easier when you're using some framework like very popular jQuery. But it's good to know how the browsers work.
are you familiar with jQuery?
its a javascript library featuring some really awesome tools.
for instance if you want to have some js action done just after your page if fully loaded and all DOM elements are created (to avoid those annoying exceptions) you can simply use the ready() method.
also i see you want to attach click \ change events jQuery takes care of this too :) and you don't have to worry about all those cross-browser issues.
take a look at jQuery selectors to make your life easier when attempting to fetch an element.
well thats it, just give it a shot, its has a very intuitive API and a good documentation.