I'm trying to write some JavaScript for my website that will select all the forms on a page, add an event listener for when they are submitted, and then route the values submitted to the main action page and an additional alternate page.
Here's what I have so far:
Send.php:
<script type="text/javascript">
function post_to_url(path, params, method) {
method = method || "post"; // Set method to post by default, if not specified.
// The rest of this code assumes you are not using a library.
// It can be made less wordy if you use one.
var form = document.createElement("form");
form.setAttribute("method", method);
form.setAttribute("action", path);
for(var key in params) {
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
document.body.appendChild(form); // Not entirely sure if this is necessary
form.submit();
}
function addEvent(elm, evType, fn, useCapture) {
if (elm.addEventListener) {
elm.addEventListener(evType, fn, useCapture);
return true;
}
else if (elm.attachEvent) {
var r = elm.attachEvent('on' + evType, fn);
return r;
}
else {
elm['on' + evType] = fn;
}
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
}
else {
window.onload = function() {
oldonload();
func();
}
}
}
function addSubmits() {
var fs=document.forms;
for(var i=0;i<fs.length;i++){
//addEvent(fs[i],"submit",post_to_url('http://www.gwtwg.com/recieve.php', {'email' : 'test.com'}), false)
}
}
addLoadEvent(addSubmits);
</script>
<form method="post" action="<?php echo $_SERVER["PHP_SELF"]; ?>">
<input type="text" name="email" />
<input type="submit" value="Submit!" />
</form>
Right now the script just freezes up, but if I replace: "post_to_url('http://www.gwtwg.com/recieve.php', {'email' : 'test.com'})"
with:
"alert("foobar")"
I get an alert when I load the page and when the form is submitted. (It should only occur when the button is submitted).
Any thoughts on whats wrong?
So, the problem here is really that you're not using any JS library. Personally I would recommend that you use jQuery, but any of the major ones will work. All of the functions you showed have more robust equivalents in the various JS libraries, so it's silly to try and debug them when you can just replace them with better-tested, more robust versions, for free.
If you were using jQuery, here's how (I think) you could do what you want to do:
var URL = "http://www.gwtwg.com/recieve.php"; // Define your site's URL
$(function() { // setup an onReady (similar to onLoad) handler
$("form") // get all forms on the page
.submit(function() { // hook up an onSubmit handler to them
var params = $(this).serialize(); // get this form's data
$.post(URL, paprams);// POST the data your server
});
});
Now, I haven't actually tested any of that, but it should work (and even if it doesn't, hopefully you get the idea). Also, notice how much shorter it is? Yet another benefit. The important thing though is, if you use established code, that's been reviewed by hundreds of eyeballs, you won't spend time re-inventing the wheel. Also, far more importantly, you won't waste time begging people on the Internet to help you debug your version of that wheel ;-)
Related
Good day,
I'm trying to configure the AdWords conversion code for my website and I can't find any of the information I'm looking for in Google documentation. Ultimatly, I want to track a onClick event on the submit input of the form.
I want to know what should I include in my onClick event, since the send button doesn't lead to another page, but have an AJAX loading?
My AdWords tracking code is :
<script type="text/javascript">
/* <![CDATA[ */
goog_snippet_vars = function() {
var w = window;
w.google_conversion_id = 12345678;
w.google_conversion_label = "abcDeFGHIJklmN0PQ";
w.google_remarketing_only = false;
}
// DO NOT CHANGE THE CODE BELOW.
goog_report_conversion = function(url) {
goog_snippet_vars();
window.google_conversion_format = "3";
window.google_is_call = true;
var opt = new Object();
opt.onload_callback = function() {
if (typeof(url) != 'undefined') {
window.location = url;
}
}
var conv_handler = window['google_trackConversion'];
if (typeof(conv_handler) == 'function') {
conv_handler(opt);
}
}
/* ]]> */
</script>
<script type="text/javascript"
src="//www.googleadservices.com/pagead/conversion_async.js">
</script>
Ultimately, I was looking for the conversion to be saved only if the form is sucessfully sent (After Javascript validation). I could not use the onClick on the button, because it would save the conversion even if the form has not submited due to an invalid value in a field.
The easiest way to do so, was to add this in jQuery :
$('#contactform').submit(function(e){
if( $( this ).valid() ) {
goog_report_conversion ('http://example.com/your-link') // Change for your page link.
alert("Conversion!"); // Confirmation that the conversion has been sent. Remove after testing.
}
});
I guess it's too late, but for the future generations. There is a built-in way for AdWords to do this.
It is described in this article.
In brief you would need to change setting in AdWords it would generate you async tag, which you load with the DOM, and activate with onClick handler.
Example:
<a onclick="goog_report_conversion ('http://example.com/your-link')"
href="http://example.com/your-link">Download now!</a>
You can load de of the noscript version.
So, in you onClick event, append the img to some element on the page and it will load it and will count the conversion.
In my page there is a frame that belongs to the same domain. The content of this frame is varied and relatively unpredictable. Whenever a user clicks a button (inside the frame) that performs a post, I need to execute a function that performs some UI tasks. The problem is that I cannot edit the source of these frames for reasons beyond my control. Some of these buttons are simple form submit buttons, but others do not directly submit the form, but instead have an onclick handler that performs some checks and might submit.
Here is the problem: How do I detect if one of these onclick handlers called form.submit()? If there's no handler, then obviously I can set up a handler for onsubmit(), but is not the case for all of these buttons.
This is my code so far:
function addEventBefore(element, type, before, after) {
var old = element['on' + type] || function() {};
before = before || function() {};
after = after || function() {};
element['on' + type] = function () {
before();
old();//I can't modify this old onclick handler
after();
};
}
function setup() {
console.log('setup');
}
function takedown() {
// In this method, I want to know if old() caused a form submit
console.log('takedown');
}
function $includeFrames(jQuery, selector) {
return jQuery(selector).add(jQuery('iframe').contents().find(selector));
}
var a = $includeFrames($, 'input[type="submit"], input[type="button"]').each(function() {
var elem = $(this)[0];
addEventBefore(elem, 'click', setup, takedown);
});
In the onload event of the iframe you'll need to hook up an event listener to each form in the iframed page. You need to do this on every load, as each fresh page needs new listeners.
$("#someIframe").on('load',function() {
$(this).contents().find("form").each(function() {
$(this).on('submit',function() {... your code...})
})
}
The solution that worked for me came from a friend of mine. The solution is to shim the form.submit() function.
$(function() {
var el = document.getElementById('myform');
el.submit = function(fn) {
return function() {
myFunctionGoesHere();
fn.apply(this, arguments);
};
}(el.submit);
});
Here is a working example: http://jsfiddle.net/hW6Z4/9/
I have a tricky problem with Google Chrome Browser.
I have the folowing HTML node:
<div class="result-req-chat pointer float-right" onclick="chat.addUser(this/*, other vars*/)" ><img src="/images/profile_icon_4.png" alt="" /></div>
On the click event it triggers the chat object's method
this.addUser = function(trigger_node, id, is_silent, session, show_block_message){
if(trigger_node){
this.bk_trigger_node.html = trigger_node.innerHTML;
this.bk_trigger_node.cn = trigger_node.className;
trigger_node.innerHTML = '';
jQuery(trigger_node).addClass("loader");
jQuery(trigger_node).removeClass("bpurple");
jQuery(trigger_node).removeClass("bgray");
jQuery(trigger_node).removeClass("button");
}
//alert('if this is executed then it displays the previous changes of the node');
if(trigger_node.innerHTML == ''){
this.addUserToChat(id, is_silent, session, show_block_message);
}
if(trigger_node){
trigger_node.innerHTML = this.bk_trigger_node.html;
trigger_node.className =this.bk_trigger_node.cn;
}
}
addUserToChat():
this.addUserToChat = function (id, is_silent, session, show_block_message){
var response = this.chat_tabs.addTab(id, null);
if(response.error){
callUrl("/me/chat/remove-session/id/"+id);
this.chat_tabs.removeTab(id);
if(show_block_message) alert(response.message);
}else{
this.createTabsBar();
if(!is_silent){
this.switchTab(id);
this.resetContainer(is_silent);
}
if(id == this.chat_tabs.active_tab){
this.active_chat_obj.refresh(session);
}
if(this.closed){
if(this.stop_check){
return;
}
this.resetContainer();
this.switchTab(id);
}
callUrl("/me/chat/add-session/id/"+id);
}
}
chat_tabs.addTab():
// creates and adds the a tab
this.addTab = function(id,name,user_data,session){
var exists = this.getTab(id);
if(!exists){
if(session){
var user_session_id = session.id;
var user_session_data = session.data;
}else{
var session = this.createSession(id);
if(session.error){
return session;
}
var user_session_id = session.id;
var user_session_data = session.data;
}
if(name){
var user_name = name;
}else{
var user_name = this.getName(id);
}
if(user_data){
var user_data = user_data;
}else{
var user_data = this.getData(id);
}
var ob = new Object({
user_id: id,
user_name: user_name,
user_data: user_data,
user_session_id: user_session_id,
user_session_data: user_session_data,
has_new:false,
chat_screen: new ChatScreen(session, id, user_name, user_data, this.main_user_id, this.main_user_photo)
});
this.chat_users.push(ob);
return ob;
}else{
return exists;
}
}
callUrl():
function getUrl(url){
return jQuery.ajax({ type: 'GET', url: url, async: false }).responseText;
}
The point is that the method addUserToChat() contains a syncronous Ajax call.
The problem with Chrome is that the trigger_node changes aren't displayed. If you watch with the built-in JS debuger then everithing goes ok ( even with displaying ) .Also if you uncomment the alert.
It runs on Mozilla ( latest version ).Also the Crome is the latest version.
I can observe that in the time that it waits for the ajax response, the page is unresponsive to events like hovers, tips etc.
Do you have any suggestions for this? How can I implement a workarround method?
Synchronous Ajax calls are bad practice! They stop the browser for the entire duration and fool the user into thinking something crashed. You really should change this.
To your question why you don't see the latest DOM changes:
When you change something in JavaScript the browser will not immediately change the DOM, because painting a ui element is far more expensive than painting a dozen. So modern browsers will try to change the DOM as lazy as possible.
There are, apart from performance other upsides, like
$('p').hide();
can hide all p elements at the same time although jQuery will select each and than change the css.
I cant't give you any hind of a workaround without understanding your code better, sorry. Thanks!
UPDATE:
After reading your code, I would think about adding some closures to the application. A basic concept of javascript is that functions are first class types. I personally think, that your program flow is less than ideal, and this is the area of improvement. the calls to call url should look something like this:
var callUrl = function(url, callback, interactionStoped) {
if(typeof interactionStoped != 'undefined' && interactionStoped == true) {
//add code to display some loading animation
}
jQuery.ajax({ type: 'GET', url: url, success: function(data) {
callback(data);
//remove loading animation here
} });
}
as a start. Then you refactor your getUrl calls.
Funny thing is in your code example you never use the response, so I don't know what your app is waiting for. Assuming it is something important you must handle the response always in your callback.
Try looking at your app as if it were a tree. A Parent Function or Object will call itself some child functions that handle different tasks, wich themselves will invoke other functions. Build methods that are small and do only one thing on a really small set of data / parameters.
I can't rewrite your complete code, but I hope this helps anyway.
When do you try to display/fill the trigger_node variable?
It seems a bit like you aren't executing this action in the callback-function of the AJAX-request. Note that if the request is still running while you try to check for trigger_node, it won't of course show your changes.
I'm desperate having spent well over an hour trying to troubleshoot this. I am trying to access a node in the DOM which is created from an ASP.NET control. I'm using exactly the same id and I can see that they match up when looking at the HTML source code after the page has rendered. Here's my [MODIFIED according to suggestions, but still not working] code:
ASP.NET Header
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
<script type="text/javascript">
$(document).ready(
var el = document.getElementById('<%= txtBox.ClientID %>');
el.onchange = alert('test!!');
)
</script>
</asp:Content>
ASP.NET Body
<asp:TextBox ID="txtBox" runat="server"></asp:TextBox>
Resulting Javascript & HTML from above
<script type="text/javascript">
$(document).ready(
var el = document.getElementById('MainContent_txtBox');
el.onchange = alert('test!!');
)
</script>
...
<textarea name="ctl00$MainContent$txtBox" id="MainContent_txtBox"></textarea>
I can only assume that the script is loading before the control id has been resolved, yet when I look at the timeline with Chrome's "Inspect Element" feature, it appears that is not the case. When I created a regular textarea box to test and implement the identical code (different id of course), the alert box fires.
What on earth am I missing here? This is driving me crazy >.<
EDIT: Wierd code that works, but only on the initial page load; firing onload rather than onchange. Even jQuery says that .ready doesn't work properly apparently. Ugh!!
$(document).ready(function() {
document.getElementById('<%= txtBox.ClientID %>').onchange = alert('WORKING!');
})
Assuming the rendered markup does appear in that order, the problem is that the element doesn't yet exist at the time your JavaScript is attempting to locate it.
Either move that JS below the element (preferably right at the end of the body) or wrap it in something like jQuery's document ready event handler.
Update:
In response to your edits, you're almost there but (as others have mentioned) you need to assign a function to the onchange event, not the return result of alert(). Something like this:
$(document).ready(function() {
// Might as well use jQuery to attach the event since you're already using
// it for the document ready event.
$('#<%= txtBox.ClientID %>').change(function() {
alert('Working!');
});
});
By writing onchange = alert('Working');, you were asking JavaScript to assign the result of the alert() method to the onchange property. That's why it was executing it immediately on page load, but never actually in response to the onchange event (because you hadn't assigned that a function to run onchange).
Pick up jQuery.
Then you can
$(function()
{
var el = document.getElementById('<%= txtBox.ClientID %>');
el.onclick() { alert('test!!'); }
});
Other answers have pointed out the error (attempting to access DOM nodes before they are in the document), I'll just point out alternative solutions.
Simple method
Add the script element in the HTML below the closing tag of the element you wish to access. In its easiest form, put it just before the closing body tag. This strategy can also make the page appear faster as the browser doesn't pause loading HTML for script. Overall load time is the same however, scripts still have to be loaded an executed, it's just that this order makes it seem faseter to the user.
Use window.onload or <body onload="..." ...>
This method is supported by every browser, but it fires after all content is loaded so the page may appear inactive for a short time (or perhaps a long time if loading is dealyed). It is very robust though.
Use a DOM ready function
Others have suggested jQuery, but you may not want 4,000 lines and 90kb of code just for a DOM ready function. jQuery's is quite convoluted so hard to remove from the library. David Mark's MyLibrary however is very modular and quite easy to extract just the bits you want. The code quality is also excellent, at least the equal of any other library.
Here is an example of a DOM ready function extracted from MyLibrary:
var API = API || {};
(function(global) {
var doc = (typeof global.document == 'object')? global.document : null;
var attachDocumentReadyListener, bReady, documentReady,
documentReadyListener, readyListeners = [];
var canAddDocumentReadyListener, canAddWindowLoadListener,
canAttachWindowLoadListener;
if (doc) {
canAddDocumentReadyListener = !!doc.addEventListener;
canAddWindowLoadListener = !!global.addEventListener;
canAttachWindowLoadListener = !!global.attachEvent;
bReady = false;
documentReady = function() { return bReady; };
documentReadyListener = function(e) {
if (!bReady) {
bReady = true;
var i = readyListeners.length;
var m = i - 1;
// NOTE: e may be undefined (not always called by event handler)
while (i--) { readyListeners[m - i](e); }
}
};
attachDocumentReadyListener = function(fn, docNode) {
docNode = docNode || global.document;
if (docNode == global.document) {
if (!readyListeners.length) {
if (canAddDocumentReadyListener) {
docNode.addEventListener('DOMContentLoaded',
documentReadyListener, false);
}
if (canAddWindowLoadListener) {
global.addEventListener('load', documentReadyListener, false);
}
else if (canAttachWindowLoadListener) {
global.attachEvent('onload', documentReadyListener);
} else {
var oldOnLoad = global.onload;
global.onload = function(e) {
if (oldOnLoad) {
oldOnLoad(e);
}
documentReadyListener();
};
}
}
readyListeners[readyListeners.length] = fn;
return true;
}
// NOTE: no special handling for other documents
// It might be useful to add additional queues for frames/objects
else {
if (canAddDocumentReadyListener) {
docNode.addEventListener('DOMContentLoaded', fn, false);
return true;
}
return false;
}
};
API.documentReady = documentReady;
API.documentReadyListener = documentReadyListener;
API.attachDocumentReadyListener = attachDocumentReadyListener;
}
}(this));
Using it for your case:
function someFn() {
var el = document.getElementById('MainContent_txtBox');
el.onclick = function() { alert('test!!');
}
API.attachDocumentReadyListener(someFn);
or an anonymous function can be supplied:
API.attachDocumentReadyListener(function(){
var el = document.getElementById('MainContent_txtBox');
el.onclick = function() { alert('test!!');
};
Very simple DOM ready functions can be done in 10 lines of code if you just want one for a specific case, but of course they are less robust and not as reusable.
I have to submit two forms; one is on the page, while the other is coming through combo change event using jquery. I have to get both of these forms values on the same page. Please help me I have already wasted much time.
My code looks like this:
this function which i'm calling on the button click to submit both forms
function forms_submit()
{
document.form1.submit();
document.form2.submit();
}
You cannot submit two forms.. since your are in a browser, you can only end to one page.. and it depends on which form you submit.. (unless you go the ajax way)
perhaps you want to merge the forms in a single form in your HTML..
Give some more info/code to get some more specific answers..
You can use XMLHTTPRequest to send both forms, (but that requires two requests you'd be wasting instead of one)
I would recommend joining the forms together.
anyways a codesample,
btnSubmitBoth.onclick = function () {
var xmlReq,
data = getFormsData(); // needed to be in format of "something=1&sometinelse=2"
if (typeof XMLHttpRequest !== 'undefined' && (window.location.protocol !== 'file:' || !window.ActiveXObject)) {
xmlReq = new XMLHttpRequest();
} else {
try {
xmlReq = new ActiveXObject('Msxml2.XMLHTTP.6.0');
} catch(e) { }
try {
xmlReq = new ActiveXObject('Msxml2.XMLHTTP.3.0');
} catch(e) { }
try {
xmlReq = new ActiveXObject('Msxml2.XMLHTTP');
} catch(e) { }
}
xmlReq.open("post", url, true);
xmlReq.send(data);
}
If your submit your reason, probably I could submit your 2 forms
-----------edited--------------
var whatever ='<span id='updated_values'><input type=hidden></span>';
use $('#div').append(whatever);
Each time you select a category trigger the whatever to append the an existing form as hidden form type, you need to catch the variables at time of form submission.
Hope this solves your issue.