There are many ways to do that, you can use WebSockets (which is the best option) to send an event to each client when a new post is made, and they will update the HTML on the page.
The other option is to implement with AJAX (not the best option, but is easier). Basically, it will send a request each X time to check if there's any update from the last request. If there are new posts, update the HTML with the content. I'll say below why this can not be the best option.
Through WebSockets
WebSockets is a huge area (support chart), and you should search some articles to understand how they work to implement your own logic. A very simple example would make use of Ratchet library to make the PHP part and Socket.io to the server.
You can use wp_insert_post hook to send a message with post details to all clients every time a new post is inserted:
function send_new_post( $post_id, $post, $update ) {
foreach ($clients as $client) {
// Sending title and url to each client connected
$client->send(json_encode(array('title' => $post->post_title, 'url' => get_permalink( $post_id ))));
}
}
add_action( 'wp_insert_post', 'send_new_post', 10, 3 );
Take a look at the Example Chat implementation with Ratchet.
Through AJAX
First of all, I do not recommend using AJAX to do this on a site with many (even not that many) clients, because you can easily overload your server making those requests every X seconds.
let timestamp = 0;
function get_updates(){
let url = '?real_time=' + timestamp;
let update = $.ajax({
type: 'POST',
url: url,
async: false,
dataType: 'json',
}).complete(function(data){
// Get the current timestamp to make the next call
timestamp = data.timestamp;
if (data.posts){
$.each(data.posts, function(index, element) {
$('.posts').append($('<div>', {
text: element.title
}));
});
}
setTimeout(function(){get_updates();}, 10000); // 30 seconds
});
}
get_updates();
The timestamp will be synchronized on the first call, to prevent a client with a different timezone from getting a different timestamp, preventing old posts from being shown twice.
Here's one basic example to return the posts. You probably should make some extra validation on the timestamp to prevent from showing a very older post or even add posts_per_page
to do not show a lot of posts, causing a lot of stress to your server:
function get_real_time_posts(){
if (!isset($_GET['real_time'])){
return;
}
// Check if it is a valid timestamp
if ( !((string) (int) $_GET['real_time'] === $_GET['real_time'])
|| !($_GET['real_time'] <= PHP_INT_MAX)
|| !($_GET['real_time'] >= ~PHP_INT_MAX)) {
return;
}
$time = intval($_GET['real_time']);
if ($time == 0) {
// First call to sync timestamps
echo json_encode(array(
'posts' => [],
'timestamp' => time()
));
die;
}
$args = array(
'post_type' => 'post',
'date_query' => array(
'after' => date('Y-m-d H:i:s', $time)
)
);
$query = new WP_Query( $args );
$posts = array();
foreach ($query->posts as $key => $value) {
$post = array(
'ID' => $value->ID,
'title' => $value->post_title,
'excerpt' => $value->post_excerpt
);
$posts[] = $post;
}
$result = array(
'posts' => $posts,
'timestamp' => time()
);
echo json_encode($result);
die;
}