There are 2 major problems.
The first is that you output the opening div directly in the shortcode. Shortcodes must never echo or output directly, they always return a HTML string.
As a result, before the post content is even displayed, this has already been sent to the browser:
<div class="blog__grid">
You’re very lucky you’re wrapping your entire post content with this, otherwise you would have noticed that the opening tag is always at the beginning.
Not only that but:
- AJAX requests that process this post will be broken
- REST API endpoints will be broken
- XMLRPC will be broken
- Sitemaps will be broken
- If your shortcode supported nested content, that content would appear after the shortcode, not inside it
- If this shortcode appears inside another shortcode, it too will be broken
- Parts of this shortcode will always appear at the start of the post content even if it isn’t at the start
You’ll get invalid XML errors or invalid JSON syntax errors for anything that doesn’t output HTML pages, and the order will be broken on those that do
But this leads to problem number 2. You never have a chance to output the closing tag
Look at this code:
return $allposts;
} ?>
</div>
Notice that if posts are found, $allposts
is returned, giving no opportunity for the closing </div>
to be sent.
So instead:
- Don’t output the opening and closing tags, assign them instead in a variable, lets call it
$output
; - Don’t return
$allposts
, add it to$output
with$output .= $allposts;
- End the function with
return $output;
And if you must use code that outputs directly, wrap it in an output buffer.