Your cart is currently empty!
Category: WordPress
Fixing Elementor Nav Menu Not Working with Lazy Loaded JavaScript (Rocket Loader / WP Rocket)
If you’re using Elementor’s Nav Menu widget and you’ve enabled JavaScript lazy loading via WP Rocket or similar tools (like Cloudflare Rocket Loader), you may run into a frustrating issue:
Your nav menu appears, but
Hover effects and hamburger menu don’t work, and dropdowns won’t open.This happens because Elementor’s JavaScript — particularly the initialization of the menu widget — gets delayed and doesn’t run when expected.
Here’s a clean and reliable fix.👇
Fully Re-initialize All Elementor Widgets After Lazy Load
<script> (function() { var maxWaitTime = 5000; // maximum 5 seconds var intervalTime = 100; // check every 100ms var waited = 0; var interval = setInterval(function() { if (typeof elementorFrontend !== 'undefined' && typeof elementorFrontend.init !== 'undefined') { console.log('Elementor frontend detected, full reinit...'); jQuery.each(elementorFrontend.documentsManager.documents, function(id, document) { if (document && typeof document.container !== 'undefined') { document.container.each(function() { elementorFrontend.elementsHandler.runReadyTrigger(jQuery(this)); }); } }); clearInterval(interval); clearTimeout(timeout); } else { waited += intervalTime; } }, intervalTime); var timeout = setTimeout(function() { console.warn('Elementor frontend not ready after 5s, giving up.'); clearInterval(interval); }, maxWaitTime); })(); </script>
This script:
- Waits up to 5 seconds for Elementor’s
elementorFrontend
object to become available. - Then manually triggers the initialization of each
elementor widget
. - Prevents duplicate runs with clean-up logic.
Step 2: Fix Duplicate Arrow Icons in Dropdown Menus
Some sites end up showing two arrow icons (
» »
or▼▼
) next to each menu item with a dropdown. This is usually caused by both Elementor and your theme or a plugin (e.g., SmartMenus) injecting arrows.Here’s the simple CSS fix:
.elementor-nav-menu .sub-arrow:nth-of-type(2) { display: none !important; }
This ensures only the first arrow icon remains visible, hiding the duplicate.
Result
After adding both the script and the optional CSS fix:
- Your nav menu works on hover.
- Dropdowns open correctly.
- Mobile hamburger menu responds after first click.
- No duplicate arrow icons.
- Waits up to 5 seconds for Elementor’s
Fixing Slow WordPress Database Queries: Missing object_id Index in wp_term_relationships
If you’re noticing high MySQL/MariaDB load, slow queries, or “Sending data” processes when running WordPress, especially on WooCommerce or sites with many posts/products, you might be hitting this problem.
Common Symptoms
When you run
SHOW PROCESSLIST;
, you might see dozens or hundreds of queries stuck like this:SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE ...- Status:
Sending data
- Time: Hundreds or thousands of seconds
- Command:
Query
- State:
Sending data
This is a clear indicator that WordPress is missing a proper index for efficient joins.
Root Cause
By default, WordPress does not create a standalone index on
object_id
inside thewp_term_relationships
table.Instead, it only sets a combined primary key:
PRIMARY KEY (object_id, term_taxonomy_id)
This works fine when WordPress is looking up by both
object_id
andterm_taxonomy_id
, but it is terrible for queries that only join onobject_id
, which is very common in themes, WooCommerce, custom post queries, and plugins.Without a separate index on
object_id
, MySQL must scan the full table or the combined key every time — leading to massive performance bottlenecks.How to Fix It
You simply need to create a separate index on
object_id
in thewp_term_relationships
table.Run this SQL command:
ALTER TABLE wp_term_relationships ADD INDEX idx_object_id (object_id);
If this still doesn’t help, in my case it reduced query time but wasn’t enough, add this to your functions.php
// Globally remove SQL_CALC_FOUND_ROWS from all WPDB queries add_filter('query', function($query) { if (stripos($query, 'SQL_CALC_FOUND_ROWS') !== false) { // You can log output if you want // error_log('Intercepted query: ' . $query); // Remove SQL_CALC_FOUND_ROWS from query $query = str_ireplace('SQL_CALC_FOUND_ROWS', '', $query); } return $query; });
Replace
wp_
with your actual WordPress table prefix if it’s different.This operation is safe and non-destructive. It will not affect existing data or WordPress functionality. It just improves query speed.
Immediate Benefits
SELECT ... LEFT JOIN wp_term_relationships ON (object_id)
becomes instant instead of dragging for minutes or hours.- Server CPU and Disk IO usage drops significantly.
- MariaDB/MySQL stops accumulating hundreds of “Sending data” processes.
- WordPress pages, product listings, and category queries load much faster.
Bonus Tip: Avoid
SQL_CALC_FOUND_ROWS
Many slow WordPress queries also use
SQL_CALC_FOUND_ROWS
, which is deprecated and inefficient.Consider refactoring your queries to:
- Run a
SELECT COUNT(*)
separately for total counts - Use
LIMIT
andOFFSET
without needingFOUND_ROWS()
This improves performance even further!
Final Thoughts
This small database tweak — adding a missing index — can save your server from extreme slowdowns.
If you’re seeingSending data
issues inSHOW PROCESSLIST;
, always check for missing indexes first.- Status:
Fix Loopback, Action-Scheduler past-due actions and REST API 403 Errors on WordPress Behind Cloudflare
If your WordPress site is behind Cloudflare, you may run into issues where WP-Cron, loopback requests, or the REST API stop working properly. This typically happens because Cloudflare blocks server-to-server requests from your own domain due to bot protection, JavaScript challenges, or rate limiting.
The Problem
WordPress internally calls its own URLs (like
wp-cron.php
or REST API endpoints) using HTTP requests. When your domain is protected by Cloudflare, those internal requests may get blocked with a 403 Forbidden response — even though everything works fine for real visitors.This breaks important features like:
- Cron jobs (
DISABLE_WP_CRON
or background tasks) - Plugin updates and checks
- Site Health REST API tests
- Some block editor functionality
What You See in Site Health
Under Tools > Site Health, you may encounter this warning:
The REST API encountered an unexpected result
The REST API is one way that WordPress and other applications communicate with the server. For example, the block editor screen relies on the REST API to display and save your posts and pages.
REST API Endpoint:
https://your-site.com/wp-json/wp/v2/types/post?context=edit
REST API Response: (403) ForbiddenAction Scheduler may show this notice:
Action Scheduler: 31 past-due actions found; something may be wrong. Read documentation
You may also see that WP-Cron or plugin update checks silently fail, especially if you’re using Cloudflare Bot Fight Mode or JS Challenge settings.
The Solution: Bypass DNS and Use Direct IP with Host Header
To fix this, we can intercept all outgoing HTTP requests from WordPress that go to
your-site.com
and force them to use the direct IP address, while still sending the proper Host header (your-site.com
).🛠 Add This Code to Your
functions.php
add_action( 'http_api_curl', function( $handle, $r, $url ) { if ( strpos( $url, 'your-site.com' ) !== false ) { // Host header curl_setopt( $handle, CURLOPT_HTTPHEADER, array( 'Host: your-site.com' ) ); // Override URL to use direct IP $ip = '167.253.159.232'; $new_url = str_replace( 'your-site.com', $ip, $url ); curl_setopt( $handle, CURLOPT_URL, $new_url ); // Optional: skip SSL verification if HTTPS curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, false ); curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, false ); } }, 10, 3 );
🧠 Why It Works
- WordPress core uses
wp_remote_get()
and similar functions to communicate with itself. - Normally, those requests resolve the domain via DNS — which goes through Cloudflare.
- With this hook, we force the request to use the real server IP, bypassing Cloudflare.
- The
Host
header ensures that WordPress still treats the request as coming toyour-site.com
.
Summary
If you’re getting 403 Forbidden errors when WordPress tries to call itself (for cron jobs, REST API, or updates), and your site is behind Cloudflare, this trick forces internal requests to talk directly to the origin server.
No more Cloudflare blockages. Just clean internal communication, like it should be.
- Cron jobs (