Tracking form submission - javascript

I'm developing tracking script which tracks some events (page view, link click, custom, ...) and sends them to API. Problem is I'm little bit stuck with form submission. So far I have following...
At first I add event listeners to every form on page:
const forms = document.getElementsByTagName("form");
for (let i = 0; i < forms.length; i++) {
forms[i].addEventListener("submit", trackFormSubmit);
}
}
And then trackFormSubmit function:
const trackFormSubmit = function(evt) {
if (evt.preventDefault) {
evt.preventDefault();
}
const form = evt.target || evt.srcElement;
const formElements = form.elements;
// parse form data payload here, not important to show
const event = {
...
};
Helper.sendEvent(event)
.then(() => {
form.submit();
})
.catch(error => {
console.log(error);
form.submit();
});
};
Helper.sendEvent function sends the event to the API. This solution works well for non-SPA websites, however it's not working great for SPA. I've tested it in React where I had a form using Redux Form library - the form submission was successfully tracked but I've received console error:
Form submission canceled because the form is not connected
The evt.preventDefault() doesn't work as expected in this case. Have someone an idea how this can be implemented?
Well I have to clarify the above statement: evt.preventDefault is called by Redux Form library so you have to handle onSubmit by your function (send data somewhere + redirect). I guess it's same in other Single-Page-Apps, there's no action where the user is sent ... default form behaviour is prevented initially.

First, make sure your ES6 arrow function trackFormSubmit is defined before your for loop that sets the event listener to every form. This could cause the code to work but without listening to the form submit.
Here is an example of setting up the event listener for every form in the document and handling their submit.
const trackFormSubmit = function(event) {
event.preventDefault();
const form = event.target || event.srcElement;
const formElements = form.elements;
alert(`Form ${event.target.name} was submited.`);
};
const forms = document.querySelectorAll("form");
forms.forEach(f=>{
f.addEventListener("submit",trackFormSubmit);
});
Here is a fiddle so you can view how this code works:
https://jsfiddle.net/k3llydev/fgL5owdj/
Also, I changed the way of setting the event listeners to a forEach way. Which is a lot more readable.
Hope this gives you at least an idea of how it would work.

Checking value of event.isDefaultPrevented solved the issue :-)

Related

Submit an HTML form by javascript and prevent default

i am using a virtual keyboard built with javascript for a user interface running on an embedded system.
Source code for the virtual keyboard can be found here : https://codepen.io/dcode-software/pen/KYYKxP
I only changed a single part in the source code. Keyboard.js file line 141-151:
case "enter":
keyElement.classList.add("keyboard__key--wide");
keyElement.innerHTML = createIconHTML("Enter");
keyElement.addEventListener("click", () => {
this.properties.value += "\n";
this._triggerEvent("oninput");
/**
* The part that i modified
*/
document.querySelector("form").submit();
});
break;
Basically, what I'm trying to do here is to submit the form on the current page when the "enter" key is pressed on the virtual keyboard. (Which works fine)
But i also want to prevent default on this submit event. (e.preventDefault()) So how can i achieve that? Thanks for your help.
Edit : I already added e.preventDefault() to the submit function. Here is the code which i handle the login form submit:
//Login
const loginForm = document.querySelector("#login-form");
loginForm.addEventListener("submit", async (e) => {
e.preventDefault();
const errorContainer = document.querySelector("#login-form-message");
errorContainer.innerHTML = "";
try {
//Submitting form
} catch (error) {
//Handle error
}
});
I found it after reading the MDN documentation.
This is the HTMLFormElement.submit() documentation: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
This document explains quite well why I have this problem.
To summarize, I need to use the requestSubmit () function instead of the submit () function since the submit() function does not trigger a submit event.
HTMLFormElement.requestSubmit() documentation: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/requestSubmit

Programmatically fill reactjs form

I am writing an userscript and I can't manage to fill a form made by reactjs. My code:
document.querySelector("#id-username").value = "name#domain.xx";
// Attempt to notify framework using input event
document.querySelector("#id-username").dispatchEvent(new Event("input", {data:"name#domain.xx"}));
// Attempt to notify framework using change event
document.querySelector("#id-username").dispatchEvent(new Event("change"));
// This doesn't help either
document.querySelector("#id-username").dispatchEvent(new Event("blur"));
// Submit the form using button (it's AJAX form)
document.querySelector("fieldset div.wrap button").click();
I entered this code into developper tools console after loading the page. However the form ignores my programatical input:
The form can be found here. The purpose of my work is to automate login to given website. I provided the specific URL, but I expect generic solution to this problem (eg. using some reactjs API) that can be applied to any reactjs form. Other users might need this solution for writing automated tests for their sites.
For Chrome version 64, there is a workaround. Otherwise the event is being ignored.
See:https://github.com/facebook/react/issues/10135#issuecomment-314441175
(Full credit to fatfisz in the link)
Code
function setNativeValue(element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
valueSetter.call(element, value);
}
}
Usage
setNativeValue(textarea, 'some text');
// you must dispatch the input event, or the value will not update !!!
textarea.dispatchEvent(new Event('input', { bubbles: true }));
Event must be emmited to ReactJS to make it register the value. In particular, the input event. It is really important to ensure that the event bubbles - React JS has only one listener at the document level, rather than on the input field. I crafted the following method to set the value for input field element:
function reactJSSetValue(elm, value) {
elm.value = value;
elm.defaultValue = value;
elm.dispatchEvent(new Event("input", {bubbles: true, target: elm, data: value}));
}

HTTP Request effects URL

Everything works fine, it submits the data to the url, and the data gets put into the database just fine. The only problem I am having is the below code is on my index.php page, it submits the data to the submitusername.php page and everything works however after it submits the data to the submitusername page the url turns into "/index.php?username=blahblahblah" this isn't a major issue but it is something I would like to learn how to fix/what is causing it.
function submitUsername(){
var submitData = new XMLHttpRequest();
var username = document.getElementById('username').value;
submitData.onreadystatechange = function() {
if(submitData.readyState == 4 && submitData.status == 200) {
callback(submitData.responseText);
}
},
submitData.open("GET", "submitusername.php?username="+encodeURIComponent(username), true);
submitData.send();
}
Nothing in the code you've provided would have the effect you describe.
It sounds like you are trying to trigger the JavaScript when a form is submitted, but the form is submitting normally and loading a new page.
You need to prevent the default behaviour of the form submission.
Change your event handler function to capture the Event object and prevent the default behaviour, then make sure you are binding the event handler appropriately.
function submitUsername(evt){
evt.preventDefault
var submitData = new XMLHttpRequest();
and
document.getElementById('myform').addEventListener('submit', submitUsername);

JQuery validation resetForm is not working

I have a very simple issue: There is a form with a clear form button. When I click on it, all fields reset. But I also have an extra validation rule: at least one additional field should be filled. After clearing, they all are empty, of course, but I don't want to see these validation messages. I want it to clear the entire form, hide all validation messages and so on. Here is my code:
$("a[data-clear]").click(function (event) {
var now = new Date();
$("#report_search section:gt(0) input").val("");
$("#includeDerived").prop("checked", true);
$("#includeApp").prop("checked", true);
$("#includeOrg").prop("checked", false);
$("input[name='FromDate']").datepicker().datepicker("setDate", now.dateAdd("year", -1));
$("input[name='ToDate']").datepicker().datepicker("setDate", now);
$("form").validate().resetForm();
event.preventDefault();
});
I have only one form on the page so multiple forms is not an issue.
Desired result: form is cleared, validation messages are not shown.
Actual result: form is cleared, validation messages persist.
Sample rule which is checking if fields are filled:
jQuery.validator.addMethod("isSufficientInfo",
function (value, element, params) {
var hasPersonInfo = $("input[name='LastName']").val() && $("input[name='FirstName']").val();
if (hasPersonInfo) {
return true;
}
var hasDocInfo = $("select[name='D']").val() && $("input[name='C']").val() && $("input[name='E']").val();
if (hasDocInfo) {
return true;
}
return $("input[name='A']").val() || $("input[name='B']").val();
}, "File some fields");
$("#IsEnoughInfo").rules("add", { isSufficientInfo: "" });
If you're still looking for the answer,
I suspect that $("form").validate() creates a new validator instance.
What you need is the previously created instance:
$("form").data("validator").resetForm()
Or store the validator in a variable:
var v = $("form").validate()
v.resetForm()
Reason for error
Your event for click event is getting propagated from button to window (inside-out). This is causing your form to trigger validation and thus you are getting the same error message, even after you call the resetForm. If you debug the validation library step by step and get to this.hideErrors then you will see that when this.hideErrors gets executed, the error messages are gone. Since the validation script hasn't finished yet, it continues with other statements, and at the end the event that got propagated is handled by the window from inside-out. The error messages are again shown as this event triggers the request on the form.
Solution
The solution is to move your call to event.preventDefault() to the top, like as shown below:
$("a[data-clear]").click(function (event) {
event.preventDefault(); // Move to top
var now = new Date();
$("#report_search section:gt(0) input").val("");
$("#includeDerived").prop("checked", true);
$("#includeApp").prop("checked", true);
$("#includeOrg").prop("checked", false);
$("input[name='FromDate']").datepicker().datepicker("setDate", now.dateAdd("year", -1));
$("input[name='ToDate']").datepicker().datepicker("setDate", now);
$("form").validate().resetForm(); // this should work now
});
Also see the updated jsfiddle sample
Give it a try and let me know if this works for you or not. I did the step-by-step debug and got to this conclusion.

JQuery preventDefault not working with delegated event

I have a feed that uses AJAX to load in posts once the document is ready. Because the elements aren't ready at code execution, I have to use delegate to bind a lot of functions.
$(posts).delegate('.edit_comment_text','keyup',function(e){
return false;
if (e.keyCode == 13 && e.shiftKey == false) {
//Post the new comment and replace the textbox with a paragraph.
var message_area = $(this).parent('.comment_message');
var new_message = $(this).val();
var comment_id = $(this).closest('.group_comment').attr('data-comment');
var url = 'functions/edit_comment.php';
var array = {
'comment':comment_id,
'message':new_message
}
$.post(url, array, function(data){
console.log(data);
$(message_area).html("");
$(message_area).text(new_message);
});
}
})
This is the code I execute on the event. I've been trying to get the browser to stop dropping down a line when the user hits enter, but this action is performed before my code is even triggered. To prove it, I put the 'return false' at the very top of the block. With that example, none of my code is run when the user hits enter, but the textarea still drops a line.
Is it something to do with JQuery's delegate that causes my function to be called after the default events? They give examples of preventing default events in their documentation, so maybe it's a version bug or something?
Anyone have any ideas?
Thanks!

Categories

Resources