I’ve expanded on this to just highlight the field group and the tab in which fields below have errors. I haven’t tested this code extensively but it should work with all of the simple fields (everything except repeater, flexible content, etc). Those may work as well but I haven’t tested them.
This is sort of the approach I’ve been needing when I start getting a lot of fields and a lot of tabs on a page template.
(function($) {
$(function() {
var TabErrorMarker = {
errorClass: 'acf-tab-validation-errors',
errors: [],
fields: [],
init: function($form) {
var self = this;
this.$form = $form;
this.$tabs = this.$form.find('.acf-tab-wrap a');
var $groups = this.$form.find('.acf-postbox');
// Find all Field groups in form and then find all tabs in form.
// Then build a tabs array where each tab keeps track of its parent field group as well as its child fields.
$groups.each(function(gIndex) {
var $group = $(this),
gKey = $group.attr('id').replace('acf-', ''),
$tabs = $group.find('.acf-tab-wrap a');
$tabs.each(function(index) {
self.addTabObject($(this), {
index: $group.index(),
$el: $group
});
});
});
// ACF Hooks for adding/removing error classes on tabs and field groups of error fields hidden behind tabs.
acf.add_filter('validation_complete', this.proxy(this.validateTabs));
acf.add_action('validation_begin', this.proxy(this.removeErrors));
acf.add_action('invalid_field', this.proxy(this.addTabError));
return this;
},
addTabObject($tabLink, group) {
// Build the tab object before handing it off to the 'this.fields' array.
var self = this,
key = $tabLink.attr('data-key'),
$label = group.$el.find('.acf-field-tab').filter('[data-key="' + key + '"]'),
$fields = $label.nextUntil('.acf-field-tab'),
tab = {
index: $tabLink.index(),
key: key,
$link: $tabLink,
$label: $label
};
$fields.each(function(fIndex) {
self.addFieldObject($(this), tab, group);
});
return tab;
},
addFieldObject($field, tab, group) {
var key = $field.attr('data-key'),
type = $field.attr('data-type'),
$input = $field.find('#acf-' + key),
field = {
index: $field.index(),
key: key,
type: type,
$input: $input,
tab: tab,
group: group
};
this.fields.push(field);
return field;
},
validateTabs: function(json, $form){
// Check for validation errors and begin looping through all tabs in the form if any.
this.errors = (json.errors || []);
if(this.errors.length) {
this.addErrors();
} else {
this.removeErrors();
}
return json;
},
addErrors: function() {
// Loop over each error, find the field related to the error and mark the parent tab with an error class.
this.errors.forEach(this.proxy(function(err, index) {
var key = this.errors[index].input.replace('acf[', '').replace(']', ''),
$field = this.$form.find('.acf-field[data-key="' + key + '"]');
this.addTabError($field, this.$form);
}));
return this;
},
removeErrors: function () {
// Loop over each tab and check if all fields below that tab are error free.
// If so, remove the error class from the tab.
var self = this;
this.$tabs.each(function(i) {
self.removeTabError($(this));
});
return this;
},
addTabError: function($field) {
// Add the error class to the tab and field group of any field that has validation errors
var index = $field.index();
key = $field.attr('data-key'),
$input = $field.find('[id="acf-' + key + '"]'),
$tabLabels = $($field.get(0), this.$form).prevAll('.acf-field-tab'),
tabKey = $tabLabels.eq(0).attr('data-key'),
$tab = $('.acf-tab-wrap a[data-key=' + tabKey + ']', this.$form),
$group = $tab.closest('.acf-postbox');
$tab.addClass(this.errorClass);
$group.addClass(this.errorClass);
return this;
},
removeTabError: function($tabLink) {
// Remove the error class to the tab and field group of any field that has validation errors
var self = this,
$tabLinks = $tabLink.parents('ul').find('li a');
// Loop over each tab link, find all fields with an error class
$tabLinks.each(function(i) {
var $tab = $(this),
$group = $tab.closest('.acf-postbox'),
$errorFields = $tab.parents('.acf-fields').children('.acf-field.acf-error');
if ($errorFields.length === 0) {
$tab.removeClass(self.errorClass);
$group.removeClass(self.errorClass);
}
});
return this;
},
proxy: function(func) {
// Simple proxy function to ensure the context of a callback function is this object's instance.
var self = this;
return function() {
return func.apply(self, arguments);
};
}
};
$(function () {
var $form = $('#wpbody form');
TabErrorMarker.init($form);
});
});
})(jQuery);
And here is the CSS to highlight the tabs and field groups that have errors.
.acf-tab-button.acf-tab-validation-errors {
border-color: #F55E4F !important;
color: #F55E4F !important;
}
.acf-postbox.acf-tab-validation-errors > .hndle {
border-color: #F55E4F !important;
color: #F55E4F !important;
}
.acf-postbox.acf-tab-validation-errors > .handlediv {
color: #F55E4F !important;
}
The result looks like the image below.
This does not work. Adding the ‘orderby’ param does not work. Adding what seems to be any other WP_Query arg does seem to work (i.e. the ‘order’ param). Is there any chance at all that the args are being tweaked after the ‘apply_filters’ for this hook?
Welcome to the Advanced Custom Fields community forum.
Browse through ideas, snippets of code, questions and answers between fellow ACF users
Helping others is a great way to earn karma, gain badges and help ACF development!
We use cookies to offer you a better browsing experience, analyze site traffic and personalize content. Read about how we use cookies and how you can control them in our Privacy Policy. If you continue to use this site, you consent to our use of cookies.