Start a new topic

Salesforce OnClick JavaScript Button alternative

The main way to start/execute Apex Batch processes is creating an OnClick JavaScript Button.

There are two issues with this:

  • The apex process (WebService) is executed synchronously (i.e. UI is blocked)
  • The Javascript is executed in the Salesforce DOM, which is now on the "don't do" list when you want to pass the Salesforce Security Review
There are few alternatives
  • Lightning Quick Actions - the only option in Lightning - but it is not available in the Classic UI.
  • Visualforce page - but you cannot pass parameters, although you can access the Id

So for a 6-8 line script, you need to create 3 files (page, controller, test) - not a great option if you have about 20 buttons to replace.

Our solution involves converting the OnClick Javascript to URL and creating a Visualforce Page with Controller to handle all buttons.

The standard alert user interface for OnClick buttons - example here is a process to re-calculate the Project Actual amounts after e.g. changing the resource prices:


I think, we improved a bit for the new UI:


Here the details + code:

The suggested code based on the Salesforce Cookbook for Apex Buttons:



var result = sforce.apex.execute("accorto.ProjectLineNS", "updateProject", 
{projectId: "{!accorto__Project__c.Id}"}); 


The replacement Button URL - calling the Visualforce ApexStart with the parameters


&r=/{!accorto__Project__c.Id}&q={!URLENCODE('Recalculate Project Actuals')}




<apex:page controller="ApexStartController" title="Start Process" 
    showHeader="false" standardStylesheets="false" docType="html-5.0" applyHtmlTag="false" applyBodyTag="false" id="a">
<html xmlns="" xmlns:xlink="">
  <apex:includeScript value="/soap/ajax/36.0/connection.js"/>
  <apex:includeScript value="/soap/ajax/36.0/apex.js"/>
  <apex:stylesheet value="{!URLFOR($Resource.SLDS214, 'assets/styles/salesforce-lightning-design-system.css')}" />
 <apex:form id="f">
  <div class="slds-modal__container">
    <div class="slds-modal__header slds-theme--info">
      <div class="slds-media__figure">
          <svg aria-hidden="true" class="slds-icon slds-icon--small">
              <use xlink:href="{!URLFOR($Resource.SLDS214, '/assets/icons/utility-sprite/svg/symbols.svg#process')}"></use>
      <h2 class="slds-text-heading--medium">{!q}</h2>

    <div class="slds-modal__content slds-p-around--medium" style="min-height:80px">
        <apex:outputPanel id="result" layout="block" rendered="{!execScript}">
            <div class="slds-spinner--brand slds-spinner slds-spinner--small" role="alert">
                <span class="slds-assistive-text">Processing</span>
                <div class="slds-spinner__dot-a"></div>
                <div class="slds-spinner__dot-b"></div>
    <div class="slds-modal__footer slds-theme--default">
      <apex:commandButton value="Cancel" action="{!doCancel}" styleClass="slds-button slds-button--neutral"/>
      <apex:commandButton value="Start" action="{!doStart}" styleClass="slds-button slds-button--brand"/>

  <apex:outputPanel id="script" layout="none" rendered="{!execScript}">
    // logistics    
    var resultId = "{!$Component.result}";
    var resultDiv = document.getElementById(resultId);
    var callback = {onSuccess: handleSuccess, onFailure: handleFailure};
    function handleSuccess(result, source) {
        resultDiv.innerText = result;
    function handleFailure(error, source) {
        resultDiv.innerText = error;
    // ApexCall
    var params = {};
    params["{!n}"] = "{!v}";
    var result = sforce.apex.execute ("{!c}", "{!m}", params, callback);


The ApexStartController.cls:


public with sharing class ApexStartController {
    public ApexStartController() {
        params = System.currentPageReference().getParameters();
        // System.debug(params);
        execScript = false;
    /** Parameter */
    public Map<String,String> params { get; set; }
    /** Question */
    public String q {
        get {
        	if (q == null) {
        		q = params.get('q');
        		if (q != null)
        			q = EncodingUtil.urlDecode(q, 'UTF-8');
        	return q;
    /** Apex Class */
    public String c {
        get { return params.get('a'); }
    /** Apex Method */
    public String m {
        get { return params.get('m'); }
    /** Parameter Name */
    public String n {
        get { return params.get('n'); }
    /** Parameter Value */
    public String v {
        get { return params.get('v'); }

    public Boolean execScript { get; set; }
    public PageReference doStart() {
        execScript = true;
        return null;

    public PageReference doCancel() {
        String r = params.get('r');
        if (r != null && r.length() > 0)
            return new PageReference(r);
        return Page.TEItemTimesheet;



  • looks nicer
  • shows Spinner while executing the Apex WebService
  • easy conversion from JavaScript to URL with minimum effort

The disadvantage of this approach

  • the URL button opens another window which needs to be closed explicitly
  • user needs to refresh/requery explicitly to see the updated values in the Salesforce UI

Unfortunately - at this point - there are no alternatives for List Buttons - neither for Lightning nor for Classic - as the function GETRECORDIDS() is only working in the OnClick Javascript context.


This seems interesting alternative to JS buttons. I want to try this but looks like 'SLDS214' is missing from code provided above. Please provide CSS too so that I can try it.



Thanks Jatin - it's the standard SLDS distribution - with the Winter release, you should just need: <apex:slds /> 

Login to post a comment