Don't wanna be here? Send us removal request.
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/cybersecurity-webinar-survey/
Cybersecurity Webinar Survey
Thank you for attending:
Your views on the program are important to us. Please provide feedback on this session by completing this survey.
Cybersecurity Webinar Survey
1. What percentage of the information was new to you?
100%
75%
50%
25%
0
2. I can use this session information:
Immediately
In 2-6 months
In 7-12 months
Never
3. Would you like to learn more about this topic?
Yes
No
4. Please rate the speaker’s knowledge of the topic:
Excellent
Good
Fair
Poor
5. Please rate the speaker’s presentation skills:
Excellent
Good
Fair
Poor
6. Please rate the content of the slides/virtual aids:
Excellent
Good
Fair
Poor
7. How accurate was the session description?
Excellent
Good
Fair
Poor
8. How did the session compare to your expectations?
Excellent
Good
Fair
Poor
9. Overall session evaluation:
Excellent
Good
Fair
Poor
10. How likely are you to recommend this session to a colleague? (with 10 being most likely to recommend) *
0
1
2
3
4
5
6
7
8
9
10
0 is not at all likely, 10 is extremely likely
11. How could we make the program better?
12. Takeaways: What was your single biggest takeaway?
13. Length / pace: The [length / pace] of the webinar was: Too Long/Slow / Just Right / Too Short/Fast
If you are human, leave this field blank.
SUBMIT RESPONSE
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/case-studies-white-papers/
Case Studies & White Papers
Credit Card Security Breach Case Study, Coach's Oats vs. Coco Jimenez, et al.
If you accept online credit card payments, you are vulnerable to a Cyberattack.
Understand the details and steps on how to protect yourself and your business from this scourge.
Click here to download this case study. Enter your name and email address in the popup form.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/accessibility-compliance/
Accessibility Compliance
As of 2017, an estimated 253 million people worldwide are visually impaired: 217 million have moderate to severe impairments and 36 million are blind. (Source: World Health Organization)
Do you provide your site visitors with an inclusive experience? Does your site comply with the The Americans with Disabilities Act of 1990? Does your site meet accessibility guidelines for the visually impaired? How about other accessibility laws and policies?
Test your site with the Web Accessibility Evaluation Tool, then come back and talk to us.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/add-remove-skip-link-from-genesis-output/
Add/Remove Skip Link From Genesis Output
Skip links are internal page links which enable accessibility navigation on a webpage. They are used by screen readers for bypassing or skipping over repetitive web page content.
A recent question on the Genesis Framework Community forum was asked to find out how to remove the primary navigation skip link from the Genesis output.
If you want to examine the Genesis code that creates the skip links, you can find it in the genesis_skip_links() function in the lib/structure/header.php file.
This function's default output is a three element array as shown here using the Kint debugger:
The Genesis developers included a 'genesis_skip_links_output' filter for manipulating this array as needed. To remove an array element using this filter, simply unset it. The result of removing the primary navigation array element is shown in this Kint dump:
Here's the code for removing the skip link array element:
View the code on Gist.
The 'genesis_skip_links_output' filter can also be used to add skip links to your site as well. Add an element to the $links array where the key is the name of the HTML element's ID you're linking to.
View the code on Gist.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/roman-numerals-in-copyright-genesis-framework/
Roman Numerals in Copyright: Genesis Framework


Problem
This article is in response to a question posted on the Studio Press Community Forum:
For the copyright in the footer, how can I change it to display as Roman numerals instead of the standard Arabic digits?
https://studiopress.community/topic/copyright-in-footer-displayed-as-roman-numerals/
Response
There is no native PHP function to convert integers to Roman numerals. To convert the Genesis Framework copyright shortcode output, we have to create our own function to convert the year and a shortcode to display the modified copyright text.
Solution
The best place to start is by copying the shortcode supplied by the Genesis Framework, renaming it, and modifying it to display the Roman numerals.
The source code for the Genesis Framework copyright shortcode can be found in the Genesis /lib/shortcodes/footer.php file. The copied and modified code is:
View the code on Gist.
The source for the number conversion function is:
View the code on Gist.
Finally, call the new shortcode.
View the code on Gist.
Since this new shortcode is based on the Genesis supplied shortcode, the attributes are the same. Here's the list:
copyright – Default: ©
first – Text/markup to place between the copyright symbol and the copyright date.
before – Text/markup to place before the copyright.
after – Text/markup to place after the copyright.
How to use
Insert the shortcode as shown in the code above into the footer. Here's an example that demonstrates the new shortcode's output:
Copyright © MMIII–MMXIX Victor M. Font Jr.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/keyboard-accessibility-formidable-forms-collapsible-sections/
Keyboard Accessibility & Formidable Forms Collapsible Sections
Formidable Forms Drag & Drop Form Builder
If you know anything about us, then you know we love Formidable Forms. It is our go to product for building any form related page or even complex applications for WordPress powered sites.
Today, many countries have some sort of accessibility laws enacted that are designed to provide full and equal enjoyment to the disabled who access our online offerings. In the United States, this is primarily the Americans with Disabilities Act of 1990 (ADA).
As a website owner, you are most likely required to comply with ADA Title III. Title III prohibits disability-based discrimination for “places of public accommodations”. Places of public accommodation generally include private businesses that are open to the public, such as restaurants, hotels, movie theaters, museums, and doctor’s offices.
U.S. Federal Courts and the U.S. Department of Justice hold that while the ADA does not specifically address web accessibility, its language is still broad enough to include websites as part of business operations. The global nature of the Internet ensures your website is a place of public accommodation.
Accessibility Standards
The World Wide Web Consortium (W3C) develops international standards for the Web: HTML, CSS, and many more.
The W3C Web Accessibility Initiative (WAI) develops standards and support materials to help us understand and implement accessibility.
We can use W3C WAI resources to make our websites, applications, and other digital creations more accessible and usable to everyone.
The W3C WAI developed the Web Content Accessibility Guidelines (WCAG). It is the international standard for web accessibility. US courts recognize WCAG 2.0, in effect since 2008, as the standard for ADA compliance.
In the European Union, the EU web accessibility directive effective in EU member states as of 23 September 2018 references WCAG 2.1 as the standard to be used.
Accessibility is Good for Business
The Business Case for Digital Accessibility describes that accessibility can drive innovation, enhance your brand, extend market reach, and minimize legal risk. It includes direct and indirect benefits of accessibility, and the risks of not addressing accessibility adequately. It provides case studies and examples that demonstrate how continued investment in accessibility is good for your organization. Please take the time to read this important resource.
Keyboard Accessibility
To accommodate people with Physical or Motor Disabilities, a key WCAG requirement is to provide keyboard accessibility to any area that requires someone to click a mouse button to display its content. This means that anywhere you click to show content also has to work by using the keyboard alone, either by pressing the return key or space bar. Examples include, but aren't limited to, drop-down menus and collapsible areas.
While the Formidable Forms developers have done a great job in making their product accessible in a general sense, there's one area that appears to have been overlooked. If you create a form with collapsible sections, those sections are not accessible from the keyboard. You can't toggle them open or closed using the keyboard alone. Luckily, there's a pretty simple fix.
Tabindex
The first step is to add tabindex="0" to the h3 element rendered as the collapsible section's title.
Collapsible Section HTML Before
Navigate to the form's customize HTML page and find the HTML source code for the collapsible section.
View the code on Gist.
Collapsible Section HTML After
Add tabindex="0" to the h3 element.
View the code on Gist.
tabindex="0" allows elements besides links and form elements to receive keyboard focus. It does not change the tab order, but places the element in the logical navigation flow, as if it were a link on the page.
Collapsible Section jQuery
Add the following jQuery to the after fields area on the form's customize HTML page.
View the code on Gist.
This jQuery monitors the h3.frm_trigger keydown event and fires the element's click() event that Formidable uses to open/close the collapsible section. The keydown event is fired for all keys, regardless of whether they produce a character value.
Since, we're monitoring a class that is part of every collapsible section, using the "this" keyword to trigger the click event ensures that this simple jQuery snippet will work for all collapsible sections on your form.
There you have it! Your collapsible sections are now keyboard accessible.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/accessibility-statement/
Accessibility Statement
Accessibility Statement for Victor Font Consulting Group, LLC.
Victor Font Consulting Group, LLC. is committed to ensuring digital accessibility for people with disabilities. We are continually improving the user experience for everyone, and applying the relevant accessibility standards.
Measures to support accessibility
Victor Font Consulting Group, LLC. takes the following measures to ensure accessibility of Victor Font Consulting Group, LLC.:
Include accessibility as part of our mission statement.
Include accessibility throughout our internal policies.
Integrate accessibility into our procurement practices.
Provide continual accessibility training for our staff.
Assign clear accessibility targets and responsibilities.
Employ formal accessibility quality assurance methods.
Include people with disabilities in our design personas
Conformance status
The Web Content Accessibility Guidelines (WCAG) defines requirements for designers and developers to improve accessibility for people with disabilities. It defines three levels of conformance: Level A, Level AA, and Level AAA. Victor Font Consulting Group, LLC. is partially conformant with WCAG 2.1 level AA. Partially conformant means that some parts of the content do not fully conform to the accessibility standard.
Additional accessibility considerations
Although our goal is WCAG 2.1 Level AA conformance, we have also applied some Level AAA Success Criteria: Images of text are only used for decorative purposes. Re-authentication after a session expires does not cause loss of data.
Feedback
We welcome your feedback on the accessibility of Victor Font Consulting Group, LLC. Please let us know if you encounter accessibility barriers on Victor Font Consulting Group, LLC.
Phone: 1-844-842-3668
Email: [email protected]
Postal Address: 7283 NC Hwy 42 W Ste 102, Raleigh, NC 27603-7530
We try to respond to feedback within 2 business days.
Compatibility with browsers and assistive technology
Victor Font Consulting Group, LLC. is designed to be compatible with the following assistive technologies:
All modern browsers and operating systems
Victor Font Consulting Group, LLC. is not compatible with:
Old or abandoned browsers, e.g., Microsoft Internet Explorer versions 10 and below, and previous versions of Google Chrome, Apple’s Safari, Mozilla Firefox, or Opera.
Mobile operating systems older than 5 years
Alternative or cross-platform browsers such as Mozilla SeaMonkey, Windows K-Meleon, Mac Omniweb and iCab, or Linux Konqueror and Epiphany; and any others out there that we haven’t heard about.
Technical specifications
Accessibility of Victor Font Consulting Group, LLC. relies on the following technologies to work with the particular combination of web browser and any assistive technologies or plugins installed on your computer:
HTML
WAI-ARIA
CSS
JavaScript
These technologies are relied upon for conformance with the accessibility standards used.
Limitations and alternatives
Despite our best efforts to ensure accessibility of Victor Font Consulting Group, LLC., there may be some limitations. Below is a description of known limitations, and potential solutions. Please contact us if you observe an issue not listed below.
Known limitations for Victor Font Consulting Group, LLC.:
3rd Party components such as chat bot: Chat bot is non-conforming because We cannot ensure the quality of 3rd party components. We are discussing this issue with 3rd party vendors and providing technical details about how to bring their components into compliance.
Old images and content: Old images may be missing ALT text. Old content may be missing title text in links. Old content was added prior to the adoption of WCAG standards. We are systematically reviewing old content for non-conformance and fixing any issues as they are discovered.
Please report any issues if you encounter them.
Assessment approach
Victor Font Consulting Group, LLC. assessed the accessibility of Victor Font Consulting Group, LLC. by the following approaches:
Self-evaluation
Evaluation report
An evaluation report for Victor Font Consulting Group, LLC. is available at: http://wave.webaim.org/report#/https://victorfont.com/.
Formal approval of this accessibility statement
This Accessibility Statement is approved by:
Victor Font Consulting Group, LLC Victor M. Font Jr. Owner
Formal complaints
We aim to respond to accessibility feedback within 2 business days, and to propose a solution within 10 business days.
This statement was created on April 20, 2019 using the W3C Accessibility Statement Generator Tool.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/optional-subscriptions/
Optional Subscriptions
If you subscribe to either the First A.I.D or Advanced care plan, you are also eligible to enroll in any or all of these options subscription services.
Analytics and Google Pack—$500 per month
The Analytics & Google Pack is one of our most valuable products for starting or repairing your existing Google search presence. Included with the Nurture and Thrive plan, it encompasses the implementation of your Google Analytics tracking, Search Console registration, Google Local page construction, index coverage issues research and correction, and more.
Site Growth Automation—$500 per month
We’ll setup, install, and manage the popular Sumo Small Business/Team package on your behalf. Package includes unlimited subscribers, basic email integrations, pro visitor targeting, pro analytics, pro design tools, auto-response email, a/b tests, email campaign, basic form templates, click triggers, and browser push notifications
Social Media Management—$500 per month
From content you send us, we’ll curate 8 social media posts (1-month of content) for Facebook and Twitter. Posts will be paired with relevant stock images.
GDPR Compliance Monitoring—first-month setup fee of $500, then $250 per month
First, we are not attorneys and cannot provide legal advice. The European Union’s General Data Protection Regulation is a BIG deal for anyone collecting personal information even from a single EU resident. We’ll setup and monitor your site to help you comply with monitoring for this far-reaching law.
The GDPR Compliance monitoring service does not include creating or editing required privacy policy language or documents necessary for compliance. The privacy policy is specifically about the relationship between your business and your customers. It’s best to contact qualified legal resources for assistance in updating your online policies. We recommend Iubenda or the Small Business Legal Academy, a membership site run by attorney Suzanne Dibble.
SEO Ranking—$150 per month
Basic SEO Coaching
SEO is all about relevant content. We’ll help you understand what it takes to create effective, SEO boosting content.
Search Engine Rank Tracking
SEO ranking gives you all of the key metrics in one place. Not only that, but it shows you who else is competing for your keyword searches, saving you the effort of going out there and looking for yourself. It also gives you a timeline of how you rank, better or worse, and tracks your ranking position for you.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/care-plan-essentials/
Care Plan Essentials
The right tools for the job
Upkeep
Dedicated support for your site includes backups, software updates, security and performance scans, spam and revision clean up, database optimization, and detailed monthly site upkeep report.
Backups
We'll make sure an up-to-date backup is always ready. Backups are stored on servers located in the United States, but if EU Data Protection compliance is required, we've got you covered, too. Let us know and we'll store your backups on servers in the EU.
Backups run daily and are kept for 90 days. If you cancel your plan with us, backups are stored for 7 days after cancellation before permanent deletion. If you want backups stored for a longer period or post cancellation, optional 3rd party storage (e.g., Dropbox, Google Drive, Amazon S3, OneDrive) is required.
Executive Dashboard & Reports
At the beginning of each month, except for the first subscription month, we'll send you a report detailing the upkeep, analytics, and keyword search activity that we performed on your site in the previous month.
Keyword search reporting is available if you've subscribed to our SEO Ranking add-on. Reports can optionally be scheduled to be sent weekly or bi-weekly. Click here to view an example of our monthly maintenance report.
The Safeguard plan includes a Basic Client Dashboard that displays 5 key metrics at the click of a button.
Our Nurture and Thrive care plans include an Executive Dashboard tailored to your business needs. Anytime you want to see how your online business is thriving, your most important metrics are just 1-click away. See example here: Executive Dashboard
Support
Your dedicated support time can be used to address a majority of your needs, such as: bug fixes, performance improvements, image optimization, content updates, design updates, newsletter curation, etc.
If you are on the Safeguard plan, you may use your 45-minute support time for a consulting call.
Training
Professional Training Videos: Unlimited access to step by step video tutorials on managing your content.
Monthly Newsletter: Our monthly newsletter includes targeted articles specific to website growth and digital marketing.
Webinars: Coming soon! Live and recorded video workshops where we explore topics such as email marketing, search engine optimization, blogging, lead capture, and more.
Consulting
Consulting Calls (included in the Advanced plan): Sessions designed to get hands-on with your website growth strategy, live editing, or any other type of training needs. We review website performance, talk about your business goals, and lay out the path to help you use your website to its fullest potential.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/shop/advanced-care-plan/
Advanced Care Plan
Tour advanced care subscription:
*Required for eCommerce, Membership, advanced functionality, and high volume sites
Software Updates$mdash;WordPress Core and Plugins
Manual Website Check after Updates
Daily Website Backups w/ Restoration
Daily Security Monitoring
Daily Uptime Monitoring
Database Optimization and SPAM cleanup
Monthly Performance Scan
Email Support Ticket Desk
60-Minutes Included
Monthly Maintenance Report w/ Google Analytics report
Cancel Anytime, No Long-Term Contract
30-min Consulting Session every month with phone support
Priority support desk
Exclusive video training library
Exclusive training webinars
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/shop/first-aid-care-plan/
First A.I.D. Care Plan
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/about-care-plans/
About Care Plans
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/care-plan-consult/
WP Care Market Consultation
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/lets-observe-a-few-facts/
Let’s observe a few facts

John is your hardest working employee.
John labors 24/7 every day of the year, never asks for time off, gets sick, or complains that he’s tired. Even You have to sleep once in a while!
John's work/life balance is all work and no play, and he's happy about it.
John continually generates leads, drives conversions, and produces revenue (or at least he should).
John is a primary channel through which customers, suppliers, potential new staff, and partners can find and engage with you.
When John does his job, he is a profit center, not a cost center; he earns you more than you spend on customer acquisition.
John is indispensable to your on-going success and growth, and it's critical that he remains available to your audience without disruption.
John is your website.
As a savvy and successful business owner, how far would you go to retain this valuable and key asset?
High potential employees (Hi-Pos) see quality as a priority in the workplace. They stand out due to their associative thinking skills—which help solve problems and drive innovation. They focus on doing a good job to satisfy clients and customers. They concentrate on improving their skills and take initiative in decision making.
As a leader, you provide direction, clarity, and resources; you do your best to understand what drives your Hi-Pos and appeal to their motives. In other words, you nurture your Superstars. You develop strategies and focus on the traits that can help them flourish to continue to produce for you and thrive. You invest in their success.
Your website is a high performing employee, or at least has the potential to become one (remember John?). It requires just as much nurturing and care as its living, breathing Superstar counterparts, if not more. You've already made an investment to bring it to life, but once it's on the job, it needs nurturing to continue to produce for you. It isn't a set it and forget it proposition. It's the public persona of you and your company. It's what attracts customers to your business. In order to thrive, it has to remain healthy, vibrant, and relevant; you have to nurture it. This all takes work and yes, continued investment for its success.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/audit-review/
Audit Review
If you've purchased one of our paid comprehensive audits, please book your review session here. Make sure to use the promo code that we sent you when you bought it. If you've lost your promo code, please contact [email protected] and let us know.
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/formidable-forms-repeaters-complex-jquery-operations/
Formidable Forms Repeaters and Complex jQuery Operations
Snapshot
"To Infinity and Beyond"—Buzz Lightyear
Formidable Forms repeater fields are a great product feature. While the repeaters work fine with standard math calculations available through Formidable's shortcodes, more complex operations, such as we're addressing in this tutorial, require custom jQuery code. Getting the code to work correctly can be somewhat elusive though. This tutorial demonstrates how to get Formidable's repeater field to work with jQuery calculations as new rows are added to the form.
An example of why this is needful is posted by Howard Jennings through the Formidable Community Forum. Mr. Jennings asked how he could hide certain dropdown options based on the value selected in another dropdown.
This is something that needs to be done in jQuery because it requires manipulating CSS in the browser in real-time, which can't be done from the server in PHP. He provided some code that he had cobbled together to demonstrate his requirement. I refactored the code and reached out to him via email. Click here to access the pastebin with the refactored code.
After Mr. Jennings received the code and verified it worked well, he then asked how to apply it to each row in a repeater. This question has more challenges than solutions. After conducting some research, I couldn't find any clear examples online to share with him.
Mr. Jennings asked a good question though, and I didn't have an answer ready at hand. After thinking about it for awhile, I decided to learn how to do this myself and write this tutorial while I studied the problem
In this tutorial, you'll learn:
How repeaters work from both the Formidable and Document Object Model (DOM) perspectives
How to overcome repeater challenges in the DOM
Writing code that works
There's also an example repeater form for you to play with that does not save entries to the database.
How Repeaters Work
Formidable Perspective
First, let's understand what repeaters are from a Formidable perspective. Architecturally, repeaters are embedded forms. Whenever a user clicks on the add or remove button to add or remove a row on the form, the repeater is actually adding or removing a form entry for the embedded form.
In this article, we'll sometimes reference the repeater as the child form. The parent form is the form that displays the repeater's content.
Repeaters consist of a form section that functions as the container for the child form. The form section is a permanent part of the parent form. It has its own field id and key.
The data capture fields you add to the repeater section are actually being added to the child form. They aren't part of the parent form, other than in appearance. So in essence, when you add a repeater to your form, you are actually building two forms at once. Maybe more, if you have multiple repeaters.
When you view a form's source code in your browser, the id of the child form is stored in a hidden field beneath the section container and section title:
A Formidable Form's Source Code as Displayed in Firefox Inspector
Let's take a closer look at the source code below. The hidden field is on line 3. The child form's id is 6 (value="6"). You would use this form id if you search for data directly in the database with phpMyAdmin. Other than being found in this hidden field, the child form ID is not referenced again in the source code. It's only used with Formidable's back-end functions
<div id="frm_field_87_container" class="frm_form_field frm_section_heading form-field "> <h3 class="frm_pos_top frm_section_spacing">test field</h3> <input type="hidden" name="item_meta[87][form]" value="6" class="frm_dnc">
Further examination of the source code reveals that repeater data rows are appear as multidimensional arrays. A multidimensional array is an array containing one or more arrays. PHP understands multidimensional arrays that are two, three, four, five, or more levels deep. With our repeaters, each field in a repeater row is notated as a multidimensional array 3-levels deep.
As an example, let's say I have a repeatable section with an id of 87. There is a text field in this repeater with an id of 89. The source code for this input field as displayed in the browser is:
<input type="text" id="field_97w00-0" name="item_meta[87][0][89]" value="" data-invmsg="Text is invalid" data-sectionid="87">
Let's break this down so we can understand what we are seeing. The input type is standard for any text field. Next is the field's id as referenced by the DOM. The DOM id is different from the Formidable field id that you see when building a form. The id in the DOM is composed of the word "field" followed by an underscore "_" plus the Formidable field's key.
In the example above, the field key for field 89 is "97w00". Yet, you see there is also a "-0" appended to the end of the DOM id beginning with "field_97w00". What do you think this means?
DOM ids must be unique for all elements on a page. So, Formidable appends the repeater row id to the end of the DOM id to ensure its uniqueness.
Let's look at the input field name. This is the most critical attribute for us to understand. It lies at the heart of the jQuery function we're going to build. This is the reference to the multidimensional array that will be used on the back end. Each array element is wrapped in square brackets [].
The first dimension "[87]" is the link to the parent form. This is the field id of the repeatable section.
name="item_meta[87][0][89]"
The second dimension is the row id.
name="item_meta[87][0][89]"
The third dimension is the id of the field that stores the data.
name="item_meta[87][0][89]"
The first and third dimensions are static. They don't change as rows are added. The second dimension increments for every row that's added. Removing a row does not change row ids after they've been assigned in the DOM.
Arrays are always indexed from 0. The first row id is always 0 in an array. Now, if I add a row to the repeater, the text field on the new row is named:
name="item_meta[87][1][89]"
See how the row id increments?
The remaining properties for this input field are value, data-invmsg, and data-sectionid. Don't be concerned with these for now. They don't come into play with our jQuery function.
DOM Perspective
Repeaters are challenging to manipulate with jQuery because of how the DOM and jQuery work together. Normally, the first line in a jQuery function is:
jQuery(document).ready(function($) )
Translated into English, this means, "Don't apply the functions in this script until the DOM is ready, then execute this code and bind it to whatever elements it affects."
A page can't be manipulated safely until the document is "ready." jQuery detects this state of readiness for you. Code included inside $( document ).ready() will only run once the DOM is ready for JavaScript code to execute.
When the DOM is loaded and ready for a new form's entry, additional repeater rows don't exist yet, only the first row does. Therefore, the jQuery function is only bound to the first row. The jQuery only runs once when the DOM has reached its ready state. You can't bind code to elements that don't exist yet.
Adding rows to a repeater does not change the DOM's ready state. Once the DOM is loaded, it's always ready until the page is refreshed and reloaded. Because new repeater rows are added through jQuery Ajax calls and Ajax does not reload the page, the DOM doesn't get reloaded. It remains in its initial "ready" state and any jQuery associated with document ready will not refire. This means that code meant to manipulate a repeater row, will only continue to work on the first row.
If the entire page was refreshed and the DOM reloaded, the code would rebind to the new rows assuming the jQuery function references the HTML elements with wild cards or variables instead of direct field names.
It's not possible to know from the onset how many rows a user may add to a repeater. So, we have to figure out some way of counting how many repeater rows we have so we can make them work with our jQuery function.
Understanding how this all fits together reveals that the greatest challenge with repeater rows can be phrased as, "If the page doesn't get refreshed, how do we get our jQuery to refire as rows are added to a repeater?"
Writing code that works
Just as jQuery can detect the DOM's ready state, it can also detect other global states and events. The event we're going to rely upon for new repeater rows is ajaxComplete(). This function fires globally whenever an ajax call completes its duty. And since it's global, we can bind code to it in document ready. But what code do we bind to it?
We've already learned that code added to document ready fires when the DOM is loaded and ready to execute JavaScript. But that code only fires once. ajaxComplete() fires every time an Ajax call finishes its job.
We want the same code to run every time a form with a repeater is loaded and when a new row is added to a repeater. Following good coding practices, we would place this code into its own function so it can be executed upon document ready and ajaxComplete(). We'll call this function repeater_row_init(). As we begin to build our functions, the basic shell looks like this:
View the code on Gist.
Since it's possible that we could build a form with multiple repeater sections, it's a good idea to pass a repeater section id to the init function so we know which repeater to initialize. It's also a good idea to pass the section ids in an array so we can initialize all of the repeaters on our form at one time by looping through the array. Now our repeater_row_init function looks like this:
View the code on Gist.
If you'll notice when you read through the code, the repeater_row_init function is calling another function named repeater_section_87_init. This function is an element of the callbacks array. Here is how you create the callbacks array:
/* setup an array of repeater callback functions */ /* you'll want a different callback for each repeater section */ var callbacks = repeater_section_87_init : function() /* there's nothing in here yet */ ;
Let's build out the callback. This is where we have to traverse the DOM and find the right repeater rows to which to apply our code. A big challenge here occurs when you delete a row from a repeater. Normally, rows are added in numerical sequence. The first row is always 0. If we add a new row, it's 1, then 2, then 3, etc.
If all we were doing were adding rows it's not difficult to construct a loop and run through it by incremental counter. However, lets say we delete row 2. Row ids don't adjust for this. Our row ids will have a gap. The 0, 1, 2, 3 now becomes 0, 1, 3. You can't depend on an incremental counter loop when there is a gap in the numbers.
Moreover, when you delete a row from a repeater, Ajax is not invoked. So, there's no ajaxComplete() event to hook into. It doesn't matter, though. We can safely ignore delete events and there's no impact to our code if we do. Our only concern then is adding new rows.
This is the source code from a repeater field where I've deleted a row in the middle. There are 2 rows remaining: 0 and 2.
<div id="frm_field_87_container" class="frm_form_field frm_section_heading form-field "> <h3 class="frm_pos_top frm_section_spacing">test field</h3> <input type="hidden" name="item_meta[87][form]" value="6" class="frm_dnc"> <div id="frm_section_87-0" class="frm_repeat_sec frm_repeat_87 frm_first_repeat frm_grid_container"> <input type="hidden" name="item_meta[87][row_ids][]" value="0"> <input type="hidden" name="item_meta[87][0][0]" value=""> <div id="frm_field_89-87-0_container" class="frm_form_field form-field frm_top_container frm_field_89_container"> <label for="field_97w00-0" class="frm_primary_label">Text <span class="frm_required"></span> </label> <input type="text" id="field_97w00-0" name="item_meta[87][0][89]" value="" data-invmsg="Text is invalid" data-sectionid="87"> </div> <div class="frm_form_field frm_hidden_container frm_repeat_buttons frm "> <a href="#" class="frm_add_form_row frm_button" data-parent="87" aria-label="Add"><i class="frm_icon_font frm_plus_icon"> </i> Add</a> <a href="#" class="frm_remove_form_row frm_button frm_hidden" data-key="0" data-parent="87" aria-label="Remove"><i class="frm_icon_font frm_minus_icon"> </i> Remove</a> </div> </div> <div id="frm_section_87-2" class="frm_repeat_sec frm_repeat_87 frm_grid_container frm-fade-in"> <input type="hidden" name="item_meta[87][row_ids][]" value="2"> <input type="hidden" name="item_meta[87][2][0]" value=""> <div id="frm_field_89-87-2_container" class="frm_form_field form-field frm_top_container frm_field_89_container"> <label for="field_97w00-2" class="frm_primary_label">Text <span class="frm_required"></span> </label> <input type="text" id="field_97w00-2" name="item_meta[87][2][89]" value="" data-invmsg="Text is invalid" data-sectionid="87"> </div> <div class="frm_form_field frm_hidden_container frm_repeat_buttons frm "> <a href="#" class="frm_add_form_row frm_button" data-parent="87" aria-label="Add"><i class="frm_icon_font frm_plus_icon"> </i> Add</a> <a href="#" class="frm_remove_form_row frm_button" data-key="2" data-parent="87" aria-label="Remove"><i class="frm_icon_font frm_minus_icon"> </i> Remove</a> </div> </div> </div>
As we examine this source, we have to find an element that will clue us into the actual row id so we can determine that second dimension in the field name. There's only one element in this source code that contains the row id and it repeats for every row:
<input type="hidden" name="item_meta[87][row_ids][]" value="2">
This means we have to loop through these hidden fields, store their values, and pass those values back to the repeater section callback init function. To loop through the hidden fields and return the rowids, I've created a helper function called get_row_ids().
Good coding practice teaches that functions should only do one thing. To make sure get_row_ids() works correctly across any repeater field, we pass the section id as a parameter. Now our call back code looks like this:
/* setup an array of repeater callback functions */ /* you'll want a different callback for each repeater section */ var callbacks = repeater_section_87_init : function() var section_id = "87", rowids = get_row_ids( section_id ); /* now that we have the row ids for this section, we can apply the code to each of the correct elements */ ; function get_row_ids( section_id ) /* loop through the hidden fields that have the row_ids and push them into an array */ var row_ids = []; $('input[name^="item_meta[' + section_id + '][row_ids]"]').each(function(index, el) row_ids.push(el.value); ); /* return the rowi_ids array to the calling init function */ return row_ids;
The final initialization step is applying the code we want to each of the newly added repeater rows. For this example, we'll use a version of Mr. Jennings refactored code from the pastebin modified for our example form.
The last thing we have to wrap up is hooking the init into the ajax.Complete() function. For this, we have to know which repeater section invoked the Ajax call so we can execute the correct init callback.
Since ajaxComplete() fires globally for all Ajax calls, there has to be a way for us to determine which element on our page invoked the call. This part is very technical and requires a much deeper explanation than I want to provide in this article, but suffice it to say that you can find the repeater row id in the ajax.Complete() "event" object. It's nested 5-levels deep and not easy to find. Rather than explaining it all here, here's the code tied to ajax.Complete():
/* initialize the repeater for every added row */ $( document ).ajaxComplete( function( event, xhr, settings ) var complete_event = jQuery.makeArray(event); var active_elem = complete_event[0]['target']['activeElement']['dataset']['parent'], repeater_section = [active_elem]; if (active_elem == '87') repeater_row_init( repeater_section ); ); });
The completed code as written for the demo form below is:
View the code on Gist.
As with any other Formidable Forms custom jQuery scripts, insert your script into the After Fields section on your form's Customize HTML page.
Now, do you want to see it work?
Working Example
The Deductible dropdown as defined in the form has five values. When each row is initialized, you'll only see 3 options. If you select "Michigan" as the state, the Deductible dropdown will display 2 options. Choose any other state, and the options return to the original three. This will continue to work for all rows added to the repeater.
Demo for Repeaters and Complex jQuery Operations
If you are human, leave this field blank.
Repeater Section: id=511
State: id = 513 Alabama Alaska Arizona Arkansas California Colorado Connecticut Delaware District of Columbia Florida Georgia Hawaii Idaho Illinois Indiana Iowa Kansas Kentucky Louisiana Maine Maryland Massachusetts Michigan Minnesota Mississippi Missouri Montana Nebraska Nevada New Hampshire New Jersey New Mexico New York North Carolina North Dakota Ohio Oklahoma Oregon Pennsylvania Rhode Island South Carolina South Dakota Tennessee Texas Utah Vermont Virginia Washington West Virginia Wisconsin Wyoming
Deductible: id = 514 $7,500 / $10,000 $5,000 / $5,000 $2,500 / $15,000 $2,500 / $2,500 $5,000 / $5,000
Add Remove
jQuery(document).ready(function ($) "use strict"; /* setup our array of repeater section ids to pass to the repeater_row_init function */ var repeater_sections = ["511"]; /* setup an array of repeater callback functions */ /* you'll want a different callback for each repeater section */ var callbacks = repeater_section_511_init : function() var rowids = get_row_ids( '511' ), // get all of the row ids new_row_id = rowids[rowids.length-1]; // get the new row id, it will always be the last array index /* now that we have the row ids for this section, we can loop through the array and apply the code to each of the correct elements */ $.each(rowids, function( index, row_id ) /* the first time through, we have to make sure the CSS is set for newly added row */ /* nothing will have been selected yet and the drop down for field 513 will have no value */ if( row_id == new_row_id ) init_new_511_repeater_row( row_id ); $('select[name="item_meta[511][' + row_id + '][513]"]').on("change", function() var val1 = $(this).val(); /* clear the current val if another state had been selected previously */ $('select[name="item_meta[511][' + row_id + '][514]"]').val(""); if (val1 == 'Michigan') /* hide the values we don't want */ $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="SWBC - 0.0"]').css('display','none'); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="FIC - 0.0"]').css('display','none'); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="Seattle - 0.002"]').css('display','none'); /* display the ones we do */ $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="Seattle - 0.0"]').css('display',''); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="FIC - .003"]').css('display',''); else /* display the values we want to see */ $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="SWBC - 0.0"]').css('display',''); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="FIC - 0.0"]').css('display',''); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="Seattle - 0.002"]').css('display',''); /* hide the values we don't want */ $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="Seattle - 0.0"]').css('display','none'); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="FIC - .003"]').css('display','none'); ); ); ; function init_new_511_repeater_row( row_id ) /* display the values we want to see */ $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="SWBC - 0.0"]').css('display',''); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="FIC - 0.0"]').css('display',''); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="Seattle - 0.002"]').css('display',''); /* hide the values we don't want */ $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="Seattle - 0.0"]').css('display','none'); $('select[name="item_meta[511][' + row_id + '][514]"]').children('option[value="FIC - .003"]').css('display','none'); function get_row_ids( section_id ) /* loop through the hidden fields that have the row_ids and push them into an array */ var row_ids = []; $('input[name^="item_meta[' + section_id + '][row_ids]"]').each(function(index, el) row_ids.push(el.value); ); /* return the rowi_ids array to the calling init function */ return row_ids; /* This is our init function */ /* the repeater_section parameter is an array of repeater section ids */ function repeater_row_init( repeater_section ) /* loop through the repeater_section array and initialize each repeater row */ $.each(repeater_section, function( index, val ) /* construct the callback function name */ var fnName = "repeater_section_" + val + "_init"; /* invoke the appropriate callback function, don't use eval here */ callbacks[fnName](); ); /* initialize the repeater for the initial load */ repeater_row_init( repeater_sections ); /* initialize the repeater for every added row */ $( document ).ajaxComplete( function( event, xhr, settings ) var complete_event = jQuery.makeArray(event); var active_elem = complete_event[0]['target']['activeElement']['dataset']['parent'], repeater_section = [active_elem]; if (active_elem == '511') repeater_row_init( repeater_section ); ); );
Submit
0 notes
Text
New Post has been published on Victor Font Consulting Group, LLC
New Post has been published on https://victorfont.com/build-custom-gutenberg-blocks-with-advanced-custom-fields-blocks-feature/
Custom Gutenberg Blocks w/ Advanced Custom Fields Blocks Feature
Snapshot
Johannes Gutenberg
Two ways that you can create new content blocks for Gutenberg are to write the code yourself in JavaScript or use the new Advanced Custom Fields Blocks feature.
At the the time of this writing, the feature is available in ACF Pro V. 5.8 and higher. ACF Pro V. 5.8 is available as Beta-3. When Beta testing is complete and the ACF development team releases the Blocks feature into the wild, their plan is to include it in both the free and PRO versions.
The ACF Blocks feature provides a very easy way for building new content blocks. You don't have to be a JavaScript expert and you certainly don't have to drastically alter your development workflow. You get to use what you already know and, as Emeril Lagasse says, "kick it up a notch. BAM!!!"
ACF blocks are especially suitable when building content blocks for client websites where the blocks are meant to be used only on that website. If you are building blocks for a plugin that will be distributed to the world-at-large, the preferred coding method remains JavaScript.
This article shows you how I created an ACF content block for the "Our Captains" page on the Fish-Tale Marina website. In this tutorial, you will learn how to:
Register your ACF Gutenberg Block with PHP
Build the editor block with the ACF Field Group user interface
Render your block on the front end using PHP, HTML, and CSS
Oh yes, did I mention that we're going to be using CSS Grid for the content layout?
Let's begin…
Register your ACF Gutenberg Block with PHP
The first step in creating your ACF content block is registering it in PHP. ACF blocks are created with a call to acf_register_block(). Here is the source I created for the Fish-Tale Marina Boat Captain block.
View the code on Gist.
The documentation for the acf_register_block() can be found here: https://www.advancedcustomfields.com/resources/acf_register_block/
I really want to use a svg icon for this block. The documentation says it's feasible, but the feature doesn't work yet. You also can't use your own .png or .jpg files either. So, if you want an icon on your block, stick to the WordPress Dashicons. It's the only viable option for now.
I save all ACF block registration code in an external file that I "require_once" in the theme's functions.php. This allows me to create as many blocks as I need for a site without cluttering functions.php or worrying about changing themes. The source code for this ACF block is in the theme's /lib/ directory and it's loaded in functions.php with this line of code:
View the code on Gist.
Build the editor block with the ACF Field Group user interface
Step two is to build the field group. This is the field group for this content block:
Advanced Custom Fields Field Group for Boat Captain Content Block
Creating the field group is pretty straight forward. The name and description fields are required, and the Website URL displays conditionally when the Website Name has a value.
The one thing that's different, that you won't find in ACF Versions earlier than 5.8, is that you can assign this field group in the Location section to the Gutenberg block we registered in Step 1.
ACF Field Group Assigned to Boat Captain Block
That's all there is to it, for the back end anyway. But here's a little caveat, before you can actually see your new block in Gutenberg, make sure you've completed the first step above and your block registration code is functioning. If you didn't complete the first step correctly, you will not see the block in the location drop down.
Let's test our code and see if we can use the new block to create a post. If we did everything right to this point, our new block should show up in Gutenberg's block list.
The Boat Captain Block is Available in Gutenberg
Select the block and it should be added to our post, ready to receive content:
ACF Boat Captain Gutenberg Block Ready to Accept Content
Render your block on the front end using PHP, HTML, and CSS
With all the back end work done, it's time to render the front end. When we registered the block, we included this line in the array:
'render_template' => get_stylesheet_directory() . '/lib/blocks/block-boat-captain.php
Here's the code for block-boat-captain.php render template:
View the code on Gist.
This code retrieves the field content from the database, then displays it in standard HTML. If you'll notice, this particular code is written for columns. In fact, this was originally done for a Genesis Framework child theme. The column classes are standard in the child theme's style.css.
Earlier I promised to display this content with a CSS Grid layout. Here's the source code:
View the code on Gist.
The code isn't very different from the first example. Only the classes have changed. Here's the CSS for the grid layout using mobile first design:
View the code on Gist.
There you have it! I hope you've found this to be useful.
0 notes