{"revision":"c10276e953456835cdbb1f3a60d58ae1980be353","document":{"id":622,"path":"api-reference/scripts/client-script-best-practices.md","title":"Client-side scripting design and processing","bundle":null,"doc_type":null,"release":"australia","body":"\n# Client-side scripting design and processing\n\nWell-designed client scripts can reduce the amount of time it takes users to complete a form.\n\nProper client-side processing depends on the form loading first. Making record updates prior to form load can produce unexpected results that bypass client-side processing.\n\nIf you create client scripts to control field values on a form, you must use another method to control these field values in a list. You can:\n\n-   Disable list editing for the table.\n-   Create appropriate business rules or access controls for list editing.\n-   Create data policies.\n-   Create a separate onCellEdit client script.\n\n**Parent Topic:**[Client-side scripting](https://raw.githubusercontent.com/ServiceNow/ServiceNowDocs/australia/markdown/australia/api-reference/scripts/client-side-scripting-overview.md)\n\n## Restrict list editing\n\nIf you create UI policies or client scripts for fields on a form, you must use another method to ensure that data in those fields is similarly controlled in a list.\n\nWith the exception of onCellEdit client scripts, UI policies and client scripts apply to forms only. Use the following methods to restrict list editing when using client scripts:\n\n-   Disable list editing for the table.\n-   Create appropriate business rules or access controls for list editing.\n-   Create data policies.\n-   Create a separate onCellEdit client script.\n\n## Minimize server lookups\n\nUse client data as much as possible to eliminate the need for time-consuming server lookups.\n\nClient scripting uses either data available on the client or data retrieved from the server. The top ways to get information from the server are g\\_scratchpad and asynchronous GlideAjax lookup.\n\nThe primary difference between these methods is that g\\_scratchpad is sent once when a form is loaded \\(information is pushed from the server to the client\\), whereas GlideAjax is dynamically triggered when the client requests information from the server.\n\n**Note:** GlideRecord and g\\_form.getReference\\(\\) are also available for retrieving server information. However, these methods are no longer recommended due to their performance impact. Both methods retrieve all fields in the requested GlideRecord when most cases only require one field.\n\n### Retrieve server data using g\\_scratchpad\n\nThe g\\_scratchpad object passes information from the server to the client, such as when the client requires information not available on the form.\n\nFor example, if you have a client script that needs to access the field u\\_retrieve, and the field is not on the form, the data is not available to the client script. A typical solution to this situation is to place the field on the form and then always hide it with a client script or UI policy. While this solution may be faster to configure, it is slower to execute.\n\nIf you know what information the client needs from the server before the form is loaded, a display business rule can create g\\_scratchpad properties to hold this information. Theg\\_scratchpad is sent to the client when the form is requested, making it available to all client-side scripting methods. This is a very efficient means of sending information from the server to the client. However, you can only load data this way when the form is loaded. The business rule cannot be triggered dynamically. In those cases, use an asynchronous GlideAjax call.\n\nFor example, assume you open an incident and need to pass this information to the client:\n\n-   The value of the system property css.base.color\n-   Whether or not the current record has attachments\n-   The name of the caller's manager\n\nA display business rule sends this information to the client using the following script:\n\n```\ng_scratchpad.css = gs.getProperty('css.base.color');\ng_scratchpad.hasAttachments = current.hasAttachments();\ng_scratchpad.managerName = current.caller_id.manager.getDisplayValue();\n```\n\nTo access scratchpad data using a client script:\n\n```\n// Check if the form has attachments\nif (g_scratchpad.hasAttachments)\n    // do something interesting here\nelse\n    alert('You need to attach a form signed by ' + g_scratchpad.managerName);\n```\n\n### Retrieve server data using asynchronous GlideAjax\n\nAsynchronous GlideAjax allows you to dynamically request information from the server.\n\nThis script compares the support group of the CI and the assignment group of the incident by name:\n\n```\n//Alert if the assignment groups name matches the support group\nfunction onChange(control, oldValue, newValue, isLoading) {\n \n    if (isLoading)\n        return;\n \n    var ga = new GlideAjax('ciCheck');\n \n    ga.addParam('sysparm_name', 'getCiSupportGroup');\n    ga.addParam('sysparm_ci', g_form.getValue('cmdb_ci'));\n    ga.addParam('sysparm_ag', g_form.getValue('assignment_group'));\n    ga.getXML(doAlert); // Always try to use asynchronous (getXML) calls rather than synchronous (getXMLWait)\n}\n \n// Callback function to process the response returned from the server\nfunction doAlert(response) {\n \n    var answer = response.responseXML.documentElement.getAttribute(\"answer\");\n \n    alert(answer);\n}\n```\n\nThis script relies on the accompanying script include:\n\n```\nvar ciCheck = Class.create();\n \nciCheck.prototype = Object.extendsObject(AbstractAjaxProcessor, {\n \n    getCiSupportGroup: function() {\n \n        var retVal = ''; // Return value\n        var ciID   = this.getParameter('sysparm_ci');\n        var agID   = this.getParameter('sysparm_ag');\t\t\n        var ciRec  = new GlideRecord('cmdb_ci');\n \n        // If we can read the record, check if the sys_ids match\n        if (ciRec.get(ciID)) {\n            if (ciRec.getValue('support_group') == agID)\n                retVal = 'CI support group and assignment group match';\n            else\n                retVal = 'CI support group and assignment group do not match';\n \n            // Can't read the CI, then they don't match\n        } else {\n            retVal = 'CI support group and assignment group do not match';\n        }\n \n        return retVal;\n    }\n \n});\n```\n\n## Use the setValue\\(\\) displayValue parameter for reference fields\n\nWhen using setValue\\(\\) on a reference field, include the displayValue parameter to avoid additional server calls.\n\nWhen using setValue\\(\\) on a reference field, be sure to include the reference field display value as the 3rd parameter. If you set the value without the displayValue, the instance does a synchronous call to retrieve the display value for the record you specified. This extra round trip to the server can impact performance.\n\nThis example demonstrates the incorrect way to call setValue:\n\n```\nvar id = '5137153cc611227c000bbd1bd8cd2005';\n \ng_form.setValue('assigned_to', id); // Client needs to go back to the server to\n                                    // fetch the name that goes with this ID\n```\n\nInstead, include the display value as an optional parameter in setValue\\(\\):\n\n```\nvar id = '5137153cc611227c000bbd1bd8cd2005';\nvar name = 'Fred Luddy';\n \ng_form.setValue('assigned_to', id, name); // No server call required\n```\n\n## Use UI policy instead of a client script\n\nWhen possible, consider using a UI policy instead of a client script.\n\nUI policies provide these benefits over client scripts:\n\n-   UI policies have an **Order** field to allow full control over the order in which client-side operations take place.\n-   UI policies do not require scripting to make a field mandatory, read-only, or visible.\n\n**Note:** UI policies apply after client scripts.\n\n## Validating the input by using a client script\n\nAn excellent use for a client script is to validate the input from the user.\n\nThis validation improves the user experience because the user finds out if there are data issues before submitting the information.\n\nAn example of validation is to verify that the **Impact** field value is valid with the **Priority** field value. In this example, Low impact is not allowed with High priority.\n\n```\nif (g_form.getValue('impact') == '3' && g_form.getValue('priority') == '1')\n   g_form.showFieldMsg('impact', getMessage('Low impact now allowed with High priority'), 'error');\n\n```\n\n## Set client script order\n\nControl the order of execution for your client scripts using the Order field. To avoid having two or more client scripts run concurrently and then conflict, you can add an order for the scripts to run in.\n\n### Before you begin\n\nRole required: admin\n\n### About this task\n\nAdding an order to the client script creates a processing sequence, ordered from lowest to highest number. If two scripts conflict, the client script with the lower number executes first.\n\n### Procedure\n\n1.  Navigate to **All** &gt; **System Definition** &gt; **Client Script** and open an existing client script or click **New**.\n\n2.  [Configuring the form layout](https://raw.githubusercontent.com/ServiceNow/ServiceNowDocs/australia/markdown/platform-administration/configure-form-layout.md) to include the **Order** field.\n\n3.  Add a number to the order field based on what order you want it to run in relation to other client scripts.\n\n    Choose a lower number for the script you want to execute first.\n\n\n## Avoid DOM manipulation\n\nAvoid Document Object Model \\(DOM\\) manipulation if possible. It can cause a maintainability issue when browsers are updated.\n\nInstead, use the GlideForm API or consider a different approach for the solution. In general, when using DOM manipulation methods, you have to reference an element in the DOM by ID or using a CSS selector. When referencing out-of-box DOM elements, there is a risk that the element ID or placement within the DOM could change, causing the code to stop working and/or generate errors. Use forethought, caution, and have a full understanding of the risk you are incurring. Review these objects and reduce the use of DOM manipulation methods as much as possible.\n\n## Avoid global client scripts\n\nA global client script is any client script where the selected Table is Global. Global client scripts have no table restrictions, therefore they will load on every page in the system introducing browser load delay in the process.\n\nThere is no benefit to loading this kind of scripts on every page.\n\nAs an alternative, and for a more modular and scalable approach, consider moving client scripts to a base table \\(such as Task\\[task\\] or Configuration Item\\[cmdb\\_ci\\]\\) that can be derived for all the child/extending tables. This eliminates the system loading the scripts on every form in the UI - such as home pages or Service Catalog where they are rarely \\(if ever\\) needed.\n\n## Enclose code in functions\n\nEnclose the code in a client script inside a function.\n\nClient scripts without a function cause issues with variable scope. When code is not enclosed in a function, variables and other objects are available and shared to all other client-side scripts. If you are using the same variable names, it is possible that they could collide. This can lead to unexpected consequences that are difficult to troubleshoot.\n\nConsider this example:\n\n```\nvar state = \"6\";\n \nfunction onSubmit() {\n \n   if(g_form.getValue('incident_state') == state) {\n   \t\talert(\"This incident is Resolved\");\n   }\n}\n```\n\nBecause the *state* variable is not enclosed in a function, all client-side scripts, have access to it. Other scripts may also use the common variable name *state*. The duplicate names can conflict and lead to unexpected results. These issues are difficult to isolate and resolve. To avoid this issue, ensure that all code is wrapped in a function:\n\n```\nfunction onSubmit() {\n \n   var state = \"6\";\n \n   if(g_form.getValue('incident_state') == state) {\n   \t\talert(\"This incident is Resolved\");\n   }\n}\n```\n\nThis solution is much safer because the scope of the variable *state* is limited to the onSubmit\\(\\) function. Therefore, the *state* variable does not conflict with *state* variables in other client-side scripts.\n\n## Run only necessary scripts\n\nTo avoid running time-consuming scripts unnecessarily, make sure that client scripts perform only necessary tasks.\n\nThe following examples demonstrate improvements to the initial code sample. Each example demonstrates a particular enhancement to the script to improve performance and avoid unnecessary calls.\n\nRemember that client scripts have no **Condition** field. This means that onLoad\\(\\) and onChange\\(\\) scripts run in their entirety every time the appropriate form is loaded. This example is an inefficient onChange\\(\\) client script set to run when the **Configuration item** field changes.\n\n```\n//Set Assignment Group to CI's support group if assignment group is empty\nfunction onChange(control, oldValue, newValue, isLoading) {\n \n    var ciSupportGroup = g_form.getReference('cmdb_ci').support_group;\n \n    if (ciSupportGroup != '' && g_form.getValue('assignment_group) != '')\n        g_form.setValue('assignment_group', ciRec.support_group.sys_id);\n}\n```\n\nThis example improves upon the first by replacing the getReference\\(\\) or GlideRecord lookup with an asynchronous GlideAjax call.\n\n```\n//Set Assignment Group to support group if assignment group is empty\nfunction onChange(control, oldValue, newValue, isLoading) {\n \n    var ga = new GlideAjax('ciCheck');\n \n    ga.addParam('sysparm_name', 'getSupportGroup');\n    ga.addParam('sysparm_ci', g_form.getValue('cmdb_ci'));\n    ga.getXML(setAssignmentGroup);\n}\n \nfunction setAssignmentGroup(response) {\n \n    var answer = response.responseXML.documentElement.getAttribute(\"answer\");\n \n    g_form.setValue('assignment_group', answer);\n}\n```\n\nThe **isLoading** flag is the simplest way to prevent unnecessary code from taking up browser time in onChange scripts. The **isLoading** flag should be used at the beginning of any script that is not required to run when the form is loading. There is no need to run this script on a form load because the logic would have already run when the field was last changed. Adding the **isLoading** check to the script prevents it from doing a cmdb\\_ci lookup on every form load.\n\nThe **isTemplate** flag indicates that a template is loading.\n\n```\n//Set Assignment Group to CI's support group if assignment group is empty\nfunction onChange(control, oldValue, newValue, isLoading, isTemplate) {\n \n    if (isLoading)\n        return;\n \n    var ga = new GlideAjax('ciCheck');\n \n    ga.addParam('sysparm_name', 'getSupportGroup');\n    ga.addParam('sysparm_ci', g_form.getValue('cmdb_ci'));\n    ga.getXML(setAssignmentGroup);\n}\n \nfunction setAssignmentGroup(response) {\n \n    var answer = response.responseXML.documentElement.getAttribute(\"answer\");\n \n    g_form.setValue('assignment_group', answer);\n}\n\n```\n\nIf the onChange script should run during loading, use the following convention:\n\n```\nfunction onChange(control, oldValue, newValue, isLoading, isTemplate) {\n \n    if (isLoading) {}; // run during loading\n \n    // rest of script here\n \n}\n```\n\nThe newValue check tells this script to continue only if there is a valid value in the relevant field. This prevents the script from running when the field value is removed or blanked out. This also ensures that there will always be a valid value available when the rest of the script runs.\n\n```\n//Set Assignment Group to CI's support group if assignment group is empty\nfunction onChange(control, oldValue, newValue, isLoading, isTemplate) {\n \n    if (isLoading)\n        return;\n \n    if (newValue) {\n        var ga = new GlideAjax('ciCheck');\n \n        ga.addParam('sysparm_name', 'getSupportGroup');\n        ga.addParam('sysparm_ci', g_form.getValue('cmdb_ci'));\n        ga.getXML(setAssignmentGroup);\n    }\n}\n \nfunction setAssignmentGroup(response) {\n \n   var answer = response.responseXML.documentElement.getAttribute(\"answer\");\n \n   g_form.setValue('assignment_group', answer);\n}\n```\n\nTo have the script react to a value that changes after the form loads, use the newValue != oldValue check.\n\n**Note:** This example does not catch users changing a value and then changing it back to its original value.\n\n```\n//Set Assignment Group to CI's support group if assignment group is empty\nfunction onChange(control, oldValue, newValue, isLoading, isTemplate) {\n \n    if (isLoading)\n        return;\n \n    if (newValue) {\n        if (newValue != oldValue) {\n            var ga = new GlideAjax('ciCheck');\n \n            ga.addParam('sysparm_name', 'getSupportGroup');\n            ga.addParam('sysparm_ci', g_form.getValue('cmdb_ci'));\n            ga.getXML(setAssignmentGroup);\n        }\n    }\n}\n \nfunction setAssignmentGroup(response) {\n \n   var answer = response.responseXML.documentElement.getAttribute(\"answer\");\n \n   g_form.setValue('assignment_group', answer);\n}\n```\n\nIn this example, the GlideAjax call is buried one level deeper by rearranging the script to check as many things available to the client as possible before running the server calls. The script checks the assignment before executing the GlideAjax call. This prevents the server lookup when the **assignment\\_group** field is already set.\n\n```\n//Set Assignment Group to CI's support group if assignment group is empty\nfunction onChange(control, oldValue, newValue, isLoading, isTemplate) {\n \n    if (isLoading)\n       return;\n \n    if (newValue) {\n        if (newValue != oldValue) {\n            if (g_form.getValue('assignment_group') == '') {\n                var ga = new GlideAjax('ciCheck');\n \n                ga.addParam('sysparm_name', 'getSupportGroup');\n                ga.addParam('sysparm_ci', g_form.getValue('cmdb_ci'));\n                ga.getXML(setAssignmentGroup);\n            }\n        }\n    }\n}\n \nfunction setAssignmentGroup(response) {\n \n   var answer = response.responseXML.documentElement.getAttribute(\"answer\");\n \n   g_form.setValue('assignment_group', answer);\n}\n```\n\n"}}