There are many questions on sharepoint.stackexchange.com related to redirecting the user to a specific page after form submission. Most answers include A) code that adds a custom PreSaveAction-function which in turn does a window.location = “…” or executes WebForm_DoPostBackWithOptions function with a custom target destination or B) they rely on providing a customized link to the form itself with a modified Source parameter in the query string.
Option A fails at the default form validation and option B is complicated to maintain.
Let’s have a look what happens when a form is submitted. The Save button has this in it’s onclick-attribute:
if (!PreSaveItem()) return false; if (SPClientForms.ClientFormManager.SubmitClientForm('WPQ1')) return false; WebForm_DoPostBackWithOptions( new WebForm_PostBackOptions( "*long save button ID*", "", true, "", "", false, true ) )
First, PreSaveAction is executed. This function allows you to write custom validation rules or execute any kind of code before a form is submitted. After that SPClientForms.ClientFormManager.SubmitClientForm is executed, this function validates the form and submits it if the validation is successful. WebForm_DoPostBackWithOptions, a function that allows you to specify your own redirect destination, is actually never reached.
What we know so far is that SPClientForms.ClientFormManager.SubmitClientForm validates the form but you can’t specify your own redirect destination. With WebForm_DoPostBackWithOptions you can specify your own redirect destination but the default form validation is not executed.
Now you could start writing your own validations and put it into PreSaveAction which can be quite cumbersome if you have a complex form. Or, you somehow trick SPClientForms.ClientFormManager.SubmitClientForm into redirecting to a different URL. Let’s have a look into SPClientForms.ClientFormManager.SubmitClientForm to check out how the redirect-destination is determined:
(The corresponding code resides in clientforms.js, the code below is the debug-version and unimportant parts are omitted)
var _ctx = window[qualifier + "FormCtx"]; var redirectInfo = _ctx.RedirectInfo; var url = redirectInfo != null ? redirectInfo.redirectUrl : ''; var source = GetUrlKeyValue("Source"); source = source == null || source == '' ? GetUrlKeyValue("NextPage") : source; source = source == null || source == '' ? GetUrlKeyValue("NextUsing") : source; url = source != null && source != '' ? source : url; STSNavigate(url); //on-premises SharePoint 2013 Nav.navigate(url); //SharePoint Online
What is says is that if no Source query string parameter is present then navigate to the URL given in _ctx.RedirectInfo.redirectUrl. The Source parameter may or may not be present but if it present it takes precedence. We don’t want to change this parameter, though, because that would mean that we either have to update the URL programmatically when the form is loaded or that we have to change every link pointing to the form.
But what we can do is changing what STSNavigate/Nav.navigate does.
We don’t really care what those functions do internally, eventually their main purpose is to navigate to the given URL. I have written three different functions that do the job. Choose the one that suits you best:
function changeRedirect(options) { for(var i = 0, buttons = document.querySelectorAll(options.selector); i < buttons.length; i++){ buttons[i].setAttribute( "onclick", "Nav.navigate = STSNavigate = function(){"+ " window.location = '" + options.redirectTo + "'"+ "};"+ buttons[i].getAttribute("onclick") ) } } document.addEventListener("DOMContentLoaded", function(event) { changeRedirect({ selector: "input[value=Save]", redirectTo: "https://google.com" }) changeRedirect({ selector: "input[value=Cancel]", redirectTo: "https://bing.com" }) })
function changeRedirect(options) { for (var i = 0, buttons = document.querySelectorAll(options.selector); i < buttons.length; i++) { var newOnClick = function(originalOnClick) { return function(){ Nav.navigate = STSNavigate = function() { window.location = options.redirectTo } originalOnClick() } } buttons[i].onclick = newOnClick(buttons[i].onclick) } } document.addEventListener("DOMContentLoaded", function(event) { changeRedirect({ selector: "input[value=Save]", redirectTo: "https://google.com" }) changeRedirect({ selector: "input[value=Cancel]", redirectTo: "https://bing.com" }) })
NodeList.prototype.changeRedirect = function(redirectTo){ for (var i = 0; i < this.length; i++) { var newOnClick = function(originalOnClick) { return function(){ Nav.navigate = STSNavigate = function() { window.location = redirectTo } originalOnClick() } } this.onclick = newOnClick(this.onclick) } } document.addEventListener("DOMContentLoaded", function(event) { document.querySelectorAll("input[value=Save]").changeRedirect("https://google.com") document.querySelectorAll("input[value=Cancel]").changeRedirect("https://bing.com") })
All three functions change STSNavigate/Nav.navigate depending on which button has been clicked and on default forms this works for the Save/Cancel-buttons below the form as well as the buttons in the ribbon.
Happy SharePointing!