jQuery/JS How-Tos And Hints
Cache Selectors
When referencing selectors (DOM elements) in jQuery every call to $( something }
asks jQuery to rescan for the matching element, wrap it in a jQuery object, and create a new instance of something you already have in memory. You can save client/browser memory by caching the selectors as follows:
Event Listener
jQuery supports different types of listeners (change, submit, onBlur, mouseOut, etc.) and ways of assigning them. We typically utilize change and submit event listeners as users interact with a web page. Assigning a change listener to a DOM element/selector in jQuery is straightforward:
Alternatively, the .on()
handler can be used to assign the event listener:
The benefit of the .on()
listener is that we can assign multiple events to a function simultaneously:
Finally, if you want to trigger the change handler when it is initially loaded, it can be added to the change handler to manually by chaining the .trigger() event handler attachment as so:
Form Submission Redirect In Submit Button
Use the HTML "formaction" attribute in the input tag to redirect a form submission from the established action path of the form.
<form action="/path/file.cfm">
<input type="submit" value="Submit">
<input type="submit" value="Go Elsewhere" formaction="/elsewhere">
</form>
Passing JSON to jQuery
When invoking backend cfc functions via Ajax or jQuery post(), it is often easiest to provide a response to the calling method via a JSON string, which is then parsed using the .parse() function into a javascript object. In the following example, the text variable represents what would be returned from the server instead of a static variable.
Posting Data From Multiple Forms
Occasionally there are multiple forms on a page (e.g. a form in Bootstrap modal window and a form in the calling page for the window) and data from elements in the forms needs to be combined and submitted together to a backend cfc function. This can be accomplished by serializing the data in each form and concatenated into a single string:
Dynamically Populating Divs/Layers
In making pages more interactive, we typical modularize the pages by populating and updating content in various areas on the page dynamically in response to user interaction. This often necessitates the fetching and processing of back-end data. To respond to a user event and update content on the page asynchronously (to avoid an entire page refresh), we make a simple jQuery Ajax call:
We manually call the function at the end to perform the initial content population of the page when the page is loaded (if so desired). If we need any data from a form on the page, that data is serialized and passed to the invoked .cfm file, where it will be available as data elements in the "form" structure. Likewise, we could pass other non-form derived data here as well, such as data from previous Javascript actions.
Preventing Default HTML Form Submit Action
There are times where we want to process a form's submission in Javascript vs. the default handler of submitting the form's content to the file identified in the form tag's "action" parameter. For this, we typically use the preventDefault()
JS function as follows:
With the above example, we've assigned a submit (onSubmit) handler to the form with an ID of jobEmailForm and our first action was to prevent the default behavior that would result in the page being submitted to itself or an alternate location as specified in the action
parameter of the form
tag.
It should be noted that the use of preventDefault()
stops the default behavior, but doesn't stop the event from "bubbling up" the DOM. Thus, an action like clicking on a button, the click event is also called on all of the parent elements in the DOM. Typically this is not an issue, but if it is (such as if there are listeners for these events, then the stopPropagation()
function should be called.
Finally, with respect to preventDefault()
and stopPropagation()
, it should be noted that return false;
can also be called. This will have the same effect as preventDefault()
and stopPropagation()
used together (the event doesn't trigger its default action and doesn't bubble up the DOM), but also returns execution from the function that it is called in.
Miscellaneous Javascript Code
Preventing Form Validation in HTML5
For testing or other purposes, you can add the "novalidate" attribute to a form element to prevent the browser from validating the form by processing required field settings, etc.
Adding and Deleting Values from A String
Occasionally we use hidden form fields to store comma separated values (csv) to record multiple user selections and we need to be able to add and remove values from these comma separated value strings. To do this we use a couple of helper JavaScript functions where we simply pass the list and the desired value to be added or removed. Since JavaScript doesn't natively deal with lists and, instead uses arrays, we convert our lists to arrays and back again.
Simple jQuery Form Submit
Occasionally we need to submit a form in response to a button click, but need to be able to assign the target URL at the same time. This enables use to have different buttons on the form that submit the form's data to different locations. The jQuery .submit() and .attr() functions can be combined to handle this for us. Since this isn't an Ajax or .post() jQuery call, this isn't ansynchronis and is treated just like a regular form submission, where the user's browser is redirected to the specified URL.
On Submit and Other Event Listener Failures
We often utilize jQuery to assign event listeners to various DOM elements, such as on click and on submit events as follows:
Occasionally we run into issues where these event listeners fail to function but we receive no javascript errors (no console output) that explains the issue. The problem is generally one of two issues: either the event listener is assigned incorrectly or there is a structural problem with the DOM.
In the case of the event listener being assigned incorrectly, this is typically do to an incorrect selector specification through a formatting or naming issue. For example $('#updateSettingsForm')
selects the DOM element with an id value of updateSettingsForm. If that id value isn't the id value of your form element, then the on-submit event listener won't be assigned to the form. Make sure that you're using the correct selector for the correct element.
Structural problems with the DOM can be less obvious. For example, if we assigned the on-submit listener in the example above to the code below, we will get a failure and our submit won't trigger the listener.
It may not be immediately obvious, but the issue here in the example above in an invalid DOM. The browser will likely render this modal to the viewer just fine, but jQuery will have an issue in traversing the DOM due to its invalid structure. In this case, the <form> tag is within the modal's modal-body div, but the associated closing </form> tag is nested outside of the closing div for modal-body. As a result, jQuery will fail to properly assign the event listener and the submit event that should be associated with clicking on the "Update" button will never be detected and no error will be thrown.
Make sure that you are assigning your event to the appropriate selector and that your DOM is valid.
Passing Extra Data to Event Handlers
In supporting the integration of back-end and front-end services, it is often need to have the front-end pass/provide extra information to the back-end function calls, particularly when a given back-end function services multiple front-end tasks. For example, take the example of where we have a dashboard in an application that lists both candidates and jobs for a given user and we want the ability to flag some of those jobs and candidates as favorites. Ideally we would have one back-end function that persists the chosen favorite status in the candidate or job record, but that function will need to know if a candidate or job was selected and, if so, which one. By using data attributes, we can accomplish this.
The following code exemplifies looping through candidate and job records, displaying an icon associated with their favorite state (whether the record is a favorite or not). The jobs listing section also exemplifies using the Bootstrap popover() function to display HTML-formatted additional information when mousing over the job title.
In the code below we are assigning the same listener to both classes of displayed favorite icons (favorite and non-favorite) that must determine which state the record is in (favorite or non-favorite) and toggle that state by changing the icon and making a synchronous ajax call to a CFC method handler that will change the db record. It is noteworthy here that we are not using two separate functions, assigning a listener for one to the non-favorite icon class (.favorite-deselected) and another to the favorite class (.favorite-selected). This is because these listeners are assigned to elements when the DOM is created, so as we toggle the status from favorite/non-favorite, the listener will 'remember' the initial state of the element and always treat it as it was originally. Thus we assign the same function to both conditions and let the listener determine the favorite state of the element by reading the data-value attribute (which we toggle along with the icon class on each click).
Finally it should be noted in the ajax call in the sample script below, we're making the call asynchronous. We're doing this because we want to return status from the CFC method call (via the handler) that indicates if the toggling of the favorite status is successful or not. We're not going to display an error message if there is a failure, but we're also not going to toggle the icon status on the screen, falsely leading the user to think that the favorite status was changed when it wasn't.
CSS formatting is needed to handle the displaying of the favorite status:
The jQuery data() function is used to retrieve the extra data attribute fields added to the icons. In this scenario the favorite icon has data-id and data-favtype attributes added. These don't affect the HTML/browser display, but allow us to pass the data elements back to the on-click event listener that can then, in turn, pass them back to the back-end cfc functions via a jQuery post() call. The data-id attribute passes the record ID of the candidate or job and the data-favtype attribute passes a string to indicate if the record is a job or candidate record. This data is then serialized into the post feed as form fields with the following code:
Finally a handler is used to call the CFC function as follows:
The handler code shown above echos the status of the putFavoriteStatus() method call back to the synchronous ajax success function so we can toggle the display of the favorite icon if the CFC method call was successful and the database was successfully updated with the user-selected status. This handler is used for updating the favorite status of candidates, jobs, etc. by calling a single CFC method that uses the data-type value (passed as a form field in the ajax call to the handler via the serialized data) to the CFC method where the controller can determine the correct SQL calls to make to update the correct tables and records.
Monitoring a Form for Changes and Resetting Values on Error
We utilize some forms that don't incorporate a traditional 'submit' button. Instead, we use the jQuery onChange handler to listen for any changes to any of the form elements and then pass them to a controller (call back-end services) to process each change as it is made. Occasionally a selection can produce an error in the form of a conflict with another setting on the form. When possible we attempt to capture these conflicts in the view and handle conflict detection and resolution through the local Javascript. However, sometimes the conflict is only discoverable by the controller as it examines additional information related to the change (e.g. current database values). In these cases we need the ability to not only display the error message, but also reset the last changed form field back to it's original value.
To 'reset' a form field back to its original value, we utilize jQuery to store initial values in 'data' attributes ('data-initialValue') so we can then retrieve the value if we make a change to the related DOM element that produces a controller error. Here is some example code of a form being loaded into a modal where we desire the ability to reset form fields of the modal:
The code shown in the snipped below is used to loop through all of the form fields, create a 'data-initialValue' attribute, and then store the current value of the DOM element into the new attribute:
Every time a change is made (as recorded by the $('##updateDashboardSettingsForm').on('change', function(e) {}
code), we serialize the form data and submit it to the controller, referenced in the example code as putDashboardSettings.cfm. If there is no error, then we display a success message and reload the data-initialValue attributes again with the current values by re-running the above code snippet. However, if there is an error, we execute a variation of this script that looks for any form field (DOM element) that has a current value that is different than the data-initialValue attribute recorded value. Where there is a difference, the value is reset to the stored attribute value using the following code:
Enabling Autocomplete to return different data than displayed
jQuery provides a nifty feature called autocomplete that allows a user to start typing in a text field with autocomplete suggesting the finalized result of what they are trying to enter. This is similar to the suggestions Google makes as you type in a search expression. Autocomplete can be configured to return a different amount of matching suggestions and the suggestions can be pulled from server side code using Ajax. Since autocomplete is used on a normal text input, the data submitted in a form submission will be the data in the text field, but sometimes this is undesirable. For example, then autocomplete is used to complete a geographic location a user is entering, the desired value to be submitted in the form may be the unique ID value associated with the location instead of the location string.
Several method are available to handle this situation, but one of the easiest ways to create a hidden form field to pass the desired information in as part of the form submission. The following code exemplifies this solution:
The associated HTML code is as follows:
In this example, the getAutoCompleteLocations() function is called in the associated locations.cfc file to perform the location lookup in the database, based on the data provided by the user and passed in the location argument/parameter. Separately and previously a lookup of the user preferences is performed to determine what order they want the locations ordered by and is passed to this same function in the locationPrioritization argument/parameter.
Event though we are using only one autocomplete input field and the jQuery selector reference the field using the element ID, the jQuery each() function is used to assign the autocomplete functionality so that this could be expanded to additional fields on the page using a class or other selector reference in place of the element ID selector.
Example CF code for the getAutoCompleteLocations() function:
Dynamically Adding Elements
This is an example of how to dynamically add rows to a table (the table is predefined in the HTML simply as <table id="relocations"><tbody></tbody></table>
).
Removing Dynamically Added Elements
In continuing the above example, it is worth exploring how to delete a dynamically added element; in this case, a dynamically added table row. To perform this function, an icon is added in a table sell (shown in the above javascript) that jQuery assigns a delegated event listener to based on the icon style, "fa-minus-circule". For more on event delegation selectors, see "Assigning Selector Listeners to Dynamically Added Elements" later in this chapter.
Assigning Select Listeners to Dynamically Added Elements
Using the traditional selectors for listeners with the default assignment as shown below will not work for elements that are added to the DOM after the DOM has been created and the document.ready() event has been triggered. For example, the code below will attempt to display the data-relo attribute value to the parent <TR> element when an icon in a <TD> cell with a style of ".fa-minue-circle" is clicked on. If the table and the icon exist at the time the code below executes, then the code will work, but if the icon is added dynamically by JS after the DOM has rendered, then the on click event assignment will fail to bind.
To resolve this issue we need to use event delegation: the on click event assignment for the style needs to be bound to the style at the document delegate so the DOM will react to the associated selector for matching elements that exist when the DOM is created, or are added afterwards dynamically. The above code would be re-written as follows to implement event delegation:
Last updated
Was this helpful?