We use the formassembly app to build hosted webforms.
FormAssembly offers conditional rules to show/hide sections. They also offer the ability to add custom Javascript to their form.
I want to use their native functionality to hide some sections, but I need to trigger this via javascript.
I have setup this form so that when 'scholarship' = 'yes', then the payment details section is hidden.
I have added some js so that if discount code = 'comp' then amount = 0 and scholarship = 'yes'. However, this change does not cause the payment details section to be hidden.
From chrome dev console I can see that this radio button has an event handler set for clicks, which must be the reason for the difference in behavior. So I'm inquiring whether there is a clean way to call the event handler from my code?
My code is very simple, and is called by setting a value in the discount code input field.
<script>
function setPrice(base,code) {
var calcprice = base;
var lcc;
// test if code is not null
if (code){
lcc = code.toLowerCase();
}
if (lcc == 'comp'){
calcprice = 0;
}
else if (code) {
calcprice = 'Invalid Code';
}
else {
calcprice = calcprice;}
if (calcprice == 0){
document.getElementById("tfa_715").checked = true;
}
return calcprice;
}
</script>
Related
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.
Like in the image, the Facebook comment box has no submit button, when you write something and press Enter button, the comment posted.
I want to submit the comment via JavaScript that running in console, but I tried to trigger Enter event, submit event of the DOM. Could not make it work.
The current comment boxes aren't a traditional <textarea> inside of a <form>. They're using the contenteditable attribute on a div. In order to submit in this scenario, you'd want to listen to one of the keyboard events (keydown, keypress, keyup) and look for the Enter key which is keycode 13.
Looks like FB is listening to the keydown evt in this case, so when I ran this code I was able to fake submit a comment:
function fireEvent(type, element) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
evt.keyCode = 13;
evt.which = 13;
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
fireEvent('keydown', document.querySelector('[role="combobox"]._54-z span span'));
A couple of things to note about this. The class ._54-z was a class they just happened to use on my page. Your mileage may vary. Use dev tools to make sure you grab the right element (it should have the aria role "combobox"). Also, if you're looking to support older browsers, you're going to have to tweak the fireEvent function code above. I only tested the above example in the latest Chrome.
Finally, to complicate matters on your end, Facebook is using React which creates a virtual DOM representation of the current page. If you're manually typing in the characters into the combobox and then run the code above, it'll work as expected. But you will not be able to set the combobox's innermost <span>'s innerHTML to what you're looking to do and then trigger keydown. You'll likely need to trigger the change event on the combobox to ensure your message persists to the Virtual DOM.
That should get you started! Hope that helps!
Some years after, this post remains relevant and is actually the only one I found regarding this, whilst I was toying around trying to post to FB groups through JS code (a task similar to the original question).
At long last I cracked it - tested and works:
setTimeout(() => {
document.querySelector('[placeholder^="Write something"]').click();
setTimeout(() => {
let postText = "I'm a Facebook post from Javascript!";
let dataDiv = document.querySelector('[contenteditable] [data-offset-key]');
let dataKey = dataDiv.attributes["data-offset-key"].value;
//Better to construct the span structure exactly in the form FB does it
let spanHTML = `<span data-offset-key="${dataKey}"><span data-text="true">${postText}</span></span>`;
dataDiv.innerHTML = spanHTML;
let eventType = "input";
//This can probably be optimized, no need to fire events for so many elements
let div = document.querySelectorAll('div[role=presentation]')[1].parentElement.parentElement;
let collection = div.getElementsByTagName("*");
[...collection].forEach(elem => {
let evt = document.createEvent("HTMLEvents");
evt.initEvent(eventType, true, true); //second "true" is for bubbling - might be important
elem.dispatchEvent(evt);
});
//Clicking the post button
setTimeout(()=>{
document.querySelector('.rfloat button[type=submit][value="1"]').click();
},2000);
}, 4000);
}, 7000);
So here's the story, as I've learned from previous comments in this post and from digging into FB's code. FB uses React, thus changes to the DOM would not "catch on" as React uses virtual DOM. If you were to click "Post" after changing the DOM from JS, the text would not be posted. That's why you'd have to fire the events manually as was suggested here.
However - firing the right event for the right element is tricky business and has almost prevented me from succeeding. After some long hours I found that this code works, probably because it targets multiple elements, starting from a parent element of the group post, and drilling down to all child elements and firing the event for each one of them (this is the [...collection].forEach(elem => { bit). As written this can be obviously be optimized to find the one right element that needs to fire the event.
As for which event to fire, as was discussed here, I've experimented with several, and found "input" to be the one. Also, the code started working after I changed the second argument of initEvent to true - i.e. evt.initEvent(eventType, true, true). Not sure if this made a difference but I've had enough hours fiddling with this, if it works, that enough for me. BTW the setTimeouts can be played around with, of course.
(Unsuccessfully) Digging into FB's React Data Structure
Another note about a different path I tried to go and ended up being fruitless: using React Dev Tools Chrome extension, you're able to access the components themselves and all their props and states using $r. Surprisingly, this also works outside of the console, so using something like TamperMonkey to run JS code also works. I actually found where FB keeps the post text in the state. For reference, it's in a component called ComposerStatusAttachmentMentionsInputContainer that's in charge of the editor part of the post, and below is the code to access it.
$r actually provides access to a lot of React stuff, like setState. Theoritically I believed I could use that to set the state of the post text in React (if you know React, you'd agree that setState would be the right way to trigger a change that would stick).
However, after some long hours I found that this is VERY hard to do, since FB uses a framework on top of React called Draft.js, which handles all posts. This framework has it's own methods, classes, data structures and what not, and it's very hard to operate on those from "outside" without the source code.
I also tried manually firing the onchange functions attached to the components, which didn't work because I didn't have the right parameters, which are objects in the likes of editorContent and selectionContent from Draft.Js, which need to be carefully constructed using methods like Modifier from Draft.js that I didn't have access to (how the hell do you externally access a static method from a library entangled in the source code?? I didn't manage to).
Anyway, the code for accessing the state variable where the text is stored, provided you have React dev tools and you've highlighted ComposerStatusAttachmentMentionsInputContainer:
let blockMap = $r["state"].activeEditorState["$1"].currentContent.blockMap;
let innerObj = JSON.parse(JSON.stringify(blockMap)); //this is needed to get the next property as it's not static or something
let id = Object.keys(innerObj)[0]; //get the id from the obj property
console.log(innerObj[id].text); //this is it!
But as I wrote, this is pretty much useless :-)
as I wasn't able to post comments through the "normal" facebook page, I remembered that they also have the mobile version, which is on m.facebook. com, there, they still have the submit Button, so depending on your needs, this may be a good option
so, you could go to the mobile facebook post (eg https://m.facebook.com/${author}/posts/${postId}) and do
// Find the input element that saves the message to be posted
document.querySelector("input[name='comment_text']").value='MESSAGE TO POST';
// find the submit button, enable it and click it
const submitButton = document.querySelector("button[name='submit']");
submitButton.disabled = false;
submitButton.click();
Here is a working solution after 3 weeks of experimenting (using #Benjamin Solum's fireEvent function):
this version posts a comment only for the first post on the page (by using querySelector method)
this version can be used only on your personal wall (unless you change the query selectors)
function fireEvent(type, element, keyCode) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
if (keyCode !== undefined){
evt.keyCode = keyCode;
evt.which = keyCode;
}
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
// clicking the comment link - it reveals the combobox
document.querySelector(".fbTimelineSection .comment_link").click();
setTimeout(function(){
var combobox = document.querySelector(".fbTimelineSection [role='combobox']");
var spanWrapper = document.querySelector(".fbTimelineSection [role='combobox'] span");
// add text to the combobox
spanWrapper.innerHTML = "<span data-text='true'>Thank you!</span>";
var spanElement = document.querySelector(".fbTimelineSection [role='combobox'] span span");
fireEvent("blur", combobox);
fireEvent("focus", combobox);
fireEvent("input", combobox);
fireEvent("keydown", spanElement, 13); // pushing enter
},2000);
function fireEvent(type, element) {
var evt;
if(document.createEvent) {
evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true);
} else {
evt = document.createEventObject();
evt.eventType = type;
}
evt.eventName = type;
evt.keyCode = 13;
evt.which = 13;
if(document.createEvent) {
element.dispatchEvent(evt);
} else {
element.fireEvent("on" + evt.eventType, evt);
}
}
fireEvent('keydown', document.
to solve your question may you see this link, there is a example how to "Auto comment on a facebook post using JavaScript"
"Below are the steps:
Go to facebook page using m.facebook.com
Sign in and open any post.
Open developer mode in Chrome by pressing Ctrl+Shift+I
Navigate to the console.
Now, run the below script."
var count = 100;
var message = "Hi";
var loop = setInterval(function(){
var input = document.getElementsByName("comment_text")[0];
var submit = document.querySelector('button[type="submit"]');
submit.disabled = false;
input.value = message;
submit.click();
count -= 1;
if(count == 0)
{
clearInterval(loop);
}
}, 10000);
Kind regards
ref.: source page
So I am using jQuery in my reports and I have a suite of reports that all load thanks to jQuery all at once so the customer feels like the transitions are faster because they don't have to wait between each click. I want to be able to have all reports change based off of a prompt the customer uses. So if they select a specific day, all of the reports in the suite will change to that day. Or if they have a specific area they select, all of the reports go to that area. This will make it so the customer doesn't have to load the parameters in the prompt for each report. I am wonderin if there is a way to do this. I have looked and haven't found anything.
Edit..
So in my report that houses all of the iframes and the value prompt that I have named changeMonth I have this JS
<script>
var report = cognos.Report.getReport("_THIS_");
var radio = report.prompt.getControlByName('monthChange');
var currentRadioValue = radio.getValues()[0]; //Get initial value object
radio.setValidator(validateRadio); //Define function to validate prompt
function validateRadio(values) {
if (values && values.length > 0 && values[0].use != currentRadioValue.use) { //Only do anything if control has value and has changed
currentRadioValue = values[0]; //Assign new value for later comparison
for (var i=0; i<window.frames.length; i++) { //Loop through all iFrames
window.frames[i].changeValue(values[0].use); //Call the changeValue function passing in the radio button value
}
}
return true; //Indicates the prompt is valid
}
</script>
In the reports that I want iframed in I have a value prompt that is a drop down list with this code in an HTML tag.
<script>
function changeValue(str){
var report = cognos.Report.getReport("_THIS_"); //Grab a handle for the report
var control = report.prompt.getControlByName('monthChange'); //Grab a handle for the prompt control
control.addValues([{"use":str,"display":str}]); //Change the prompt to the passed in value
report.sendRequest(cognos.Report.Action.REPROMPT); //Reprompt the page
}
</script>
They were both drop down lists if that matters. I see that you listed them as radio buttons so I will try that here in a moment and let you know if that changed anything. But how I have it setup, is there something else i should be doing?
I was able to get this to work by creating a JavaScript function in each child report which changes a hidden prompt value which the query depends on and then reprompts the page. Here is the portion of the code that needs to be in every child object:
Child Report(s) Code
<script>
function changeValue(str){
var report = cognos.Report.getReport("_THIS_"); //Grab a handle for the report
var control = report.prompt.getControlByName('controlname'); //Grab a handle for the prompt control
control.addValues([{"use":str,"display":str}]); //Change the prompt to the passed in value
report.sendRequest(cognos.Report.Action.REPROMPT); //Reprompt the page
}
</script>
This utilizes the Cognos JavaScript Prompt API added in Cognos version 10.2. The functions getReport, getControlByName, addValues, and sendRequest are all functions provided by Cognos to make working with prompts in JavaScript easier. There's more info here:
Cognos JavaScript Prompt API documentation
In the container report I created a Cognos radio button value prompt. I coded the JavaScript to leverage the built-in onchange validation hook provided by Cognos in the API to run code when the radio button has changed. The code loops through all iFrames and calls the function defined above in each child report passing in the value of the radio button selected.
Container Report Code
<script>
var report = cognos.Report.getReport("_THIS_");
var radio = report.prompt.getControlByName('radio');
var currentRadioValue = radio.getValues()[0]; //Get initial value object
radio.setValidator(validateRadio); //Define function to validate prompt
function validateRadio(values) {
if (values && values.length > 0 && values[0].use != currentRadioValue.use) { //Only do anything if control has value and has changed
currentRadioValue = values[0]; //Assign new value for later comparison
for (var i=0; i<window.frames.length; i++) { //Loop through all iFrames
window.frames[i].changeValue(values[0].use); //Call the changeValue function passing in the radio button value
}
}
return true; //Indicates the prompt is valid
}
</script>
Note that in the above code the strings 'controlname' and 'radio' correspond to the Name property of the prompt controls in question. By default Cognos does not give prompt controls a name. Thus, you have to supply the name after creation. Whatever names you give them the script has to be adjusted accordingly to allow JavaScript to access their Cognos Prompt API objects.
This technique can be modified to take input from any of the variety of prompt controls available in Cognos. Additionally, in theory, the container doesn't even have to be Cognos at all. It could be a standalone Web page with controls that call the JavaScript functions in the iFrames when standard HTML onchange events fire. The only caveat is due to security restrictions browsers do not allow calling of functions within iFrames from containers that have a different domain than the iFrame. This is something to consider when designing a solution.
I have a function that runs on key tab press, it works fine when i put a javascipt alert in between the code, any kind of alert,if i remove the alert it stops working : my function
//Function to set the tab feture for focus to work properly on fields with autosuggestion(location and name)
function setFocusOnTab(name) {
var focusElement = "";
if(name == "name") {//For main contact field
if ($("#email").is(":visible")) {
$('#email').focus();
}
} else if(name == 'location_name') {//For main contact field
$("#country").focus();
} else {//For extra contact field
var outputDataCurrentVal = name.lastIndexOf('record_');
if(outputDataCurrentVal < 0) {
//ADDTIONAL CONTACT TAB
var outputDataCurrentName = name.lastIndexOf('_name_');
if(outputDataCurrentName >= 0) {
//Replacing the name to get location name.
locName = currentName.replace("_name_","_designation_");
focusElement = locName;
} else {
var outputDataCurrentLoc = name.lastIndexOf('_location_');
if(outputDataCurrentLoc >= 0) {
//Replacing the location name to get country name.
countryName = name.replace("_location_","_country_");
focusElement = countryName;
}
}
} else {
//Extra CONTACT TAB
var outputDataCurrentName = name.lastIndexOf('_name_');
if(outputDataCurrentName >= 0) {
//Replacing the name to get location name.
locName = currentName.replace("_name_","_location_");
focusElement = locName;
} else {
var outputDataCurrentLoc = name.lastIndexOf('_location_');
if(outputDataCurrentLoc >= 0) {
//Replacing the location name to get country name.
countryName = name.replace("_location_","_country_");
focusElement = countryName;
}
}
}
$("#" + focusElement).focus();
return false;
}
}
Sounds like you need something to halt your code, which is what alert() does.
You may need a callback instead.
What might be happening with alert() is that calling it causes the current window to lose focus (as focus moves to the new pop-up dialogue box), and after it's finished it re-focuses the window. This will trigger focus and blur events which might confuse your script, and in Safari the window may not re-focus at all.
It's not clear to me what you are doing here... how are you attaching this code to a tab key event? What event is supposed to be cancelled by the return false;? If you are using keypress, then that simply won't get called for the Tab key in IE, Safari or Chrome. If you are using keydown, then cancelling the event won't prevent the tabbing in Opera. And what about Shift-Tab?
Reproducing/altering browser keyboard behaviour is hard: it's much better not to if there's any other way. For making controls like a drop-down suggester work you are probably much better off just setting the declarative tabIndex on the elements concerned and letting the browser work out how to sort out the tabs from there.
I should write something on this post. I read so many blogs and post but couldn't get the right solution from anywhere else. Even in this post, I looked more detail and tried each of solution.
Finally reading the answer of bobince, I could figured out the solution. In my case, I have set the focus to another textbox ( not required type) and later when I finish my job, I set back focus to original one. So the morel story is we just need to set focus somewhere else from current element which actually done by alert and I have replaced that on by setting the focus on non required element.
We need to universally handle changed data on forms in ASP.NET MVC. Our application has ~100 forms, and the user should be prompted if they start editing a form and click on anything other than Save (i.e. something like "Your data has been changed. Click OK to return to the form, or Cancel to lose all changes.").
It looks like SO implements this (while asking a question) using JavaScript. In general, is this the best way? Also, any tips on how best to implement this?
The way I've done this is to use javascript to store the initial values of inputs when the page loads. Then I have a beforeunload handler that checks to see if any of the inputs have a different value than when the page was loaded. If any inputs are changed, it prompts the user to confirm that they want to leave the page, canceling the action if they cancel. In my submit logic, I set a flag that keeps the beforeunload check from happening so a submit doesn't get the prompt.
I suspect there is a jQuery plugin to do this, but I haven't implemented this since I started using jQuery. My earlier code used Prototype.
Edit: Couldn't find a jQuery plugin, but I could have just missed it. Here's a sample of how I might do it. Obviously, there's more that could be done. Note I wasn't able to get it to work with pure jQuery -- not sure exactly why, the popup would appear twice.
This should work with all input elements. You might want to change it to ignore/handle buttons, though. I only adjusted it to ignore a submit button (so it can post back without the popup). If other button types can cause a page unload, you may need to address that.
var CheckOnClose = function() {
this.initialize();
}
CheckOnClose.prototype = {
submitting: false,
initialize: function() {
var that = this;
window.onbeforeunload = function() { return that.checkLeavePage(); }
},
isChanged: function() {
var changed = false;
$('input:not(:submit)').each( function() {
var iv = $(this).data('initialValue');
if ($(this).val() != iv) {
changed = true;
return false;
}
});
return changed;
},
setSubmitting: function() {
this.submitting = true;
},
checkLeavePage: function() {
if (!this.submitting && this.isChanged()) {
return 'You have some unsaved changes.';
}
}
}
var checker = new CheckOnClose();
$(document).ready(function() {
$(':input:not(:submit)').each( function() {
$(this).data('initialValue',$(this).val() );
});
$(':submit').click( function() {
checker.setSubmitting();
});
});
JavaScript is your only shot for doing this. It doesn't even have to be a complicated bunch of code. All you have to do is have a global variable to flag if the form is in editing stages (var formEdited = false; would do), and then add this snippet to your page:
window.onbeforeunload = confirmExit;
function confirmExit()
{
if (formEdited)
{
return "You have attempted to leave this page. If you have made any changes to the fields without Submitting the form, your changes will be lost. Are you sure you want to exit this page?";
}
// no changes - return nothing
}