Display content from an external URL in a pop-up modal in WordPress

Display external URL in modal popup

Recently I was looking for a simple way to collect payments without having to install a full eCommerce system on a website.

After all, I just wanted to be able to sell some simple digital products, and I didn’t want the overhead of Easy Digital Downloads or WooCommerce – both for site performance and for maintenance purposes.

I also didn’t want to use a third-party solution like Gumroad, Payhip, Sellfy or SendOwl, because these tend to take people away from my site, and they become customers of that third-party service, instead of remaining my customers.

So I was thrilled to discover that WPForms (the pro version) has an integration with Stripe, so you can just set up a form with a payment field, and voila, instant payment solution.

I use WPForms extensively as my preferred forms plugin, after finding Gravity Form’s overhead a bit too steep, and not loving Ninja Forms or any of the other available forms plugins.

Showing my form in a modal

But then the next problem presented itself.

You see, I wanted to present my form in a modal, so it felt a bit like the Stripe, Paypal or Shopify modal experiences, where people stay on the page to complete the transaction.

And in fact, what I wanted was for people to be able to click a button, and have that button include a link to the content that is then loaded in the modal.

The reason I approached it this way is that my aim was to use a single form for all of my products, and just have the details and price change depending on the button.

By using pre-fill variables as part of the button’s URL, I could dynamically change the content of the form to suit each product.

So I needed a solution that:

  • Displayed a modal when a button is clicked
  • Loaded the content of a URL (including parameters) in the modal window
  • Kept the visitor on the page
  • Allowed for many such buttons on the same page (e.g. a products page)

Finding a plugin to create a modal with URL content

I looked at a bunch of plugins that do “pop-ups” or modal windows in WordPress.

NOTE: A modal and pop-up are technically slightly different things. A modal is an in-page element that overlays the main page content and contains separate content, where a pop-up is usually a separate browser window that interacts with the initiating page. However, people seem to use the terms interchangeably, so be careful when looking at solutions to make sure you find the right type.

Almost none of them came close to what I needed, specifically loading a URL with parameters.

The free plugin that came closest to what I needed was WP Post Popup, which took my URL and loaded it into a modal on button click.

The only thing it couldn’t do was handle my pre-fill parameters, and it crashed when I tried to add them to the end of the URL.

I found two paid plugins that also promised to handle external URLs, but I couldn’t test either of them without paying.

Firstly, Popup Builder have an iframe extension that claims to be able to handle external URLs, but they don’t have a trial and don’t do refunds, so I’d have to spend a minimum of USD19 just to figure out whether it would do what I needed (because their documentation is not detailed enough and I didn’t trust the “neat and tidy” response I got from their support).

I strongly suspected that 5 minutes after I bought it, I’d realise that it couldn’t do what I needed, and then I’d be out of pocket and grumpy!

The most popular popup plugin, Popup Maker, also has a “remote content” extension, but only in their Grow plan, which costs USD147 a year (or USD35 for just the remote content extension only).

I just didn’t feel like committing to paying that year after year, when WP Easy Cart, my preferred end-to-end eCommerce solution, costs slightly less than that, so if I was going to spend that much, I might as well go with a full eCommerce solution.

Any maybe I just got tired of being forced to spend money on plugins that I wasn’t even confident would do what I needed.

So I decided to see if I could create my own solution, because what I wanted didn’t seem that complicated (to me anyway).

Creating a shortcode to insert any URL into a modal

I found a handy tutorial on W3 Schools on creating a modal box using CSS and Javascript and adapted and extended it for my purposes.

I set up my code as a shortcode, so I could easily add my modal solution anywhere, and as many times as I liked on a page.

My shortcode takes a number of parameters, and inserts a simple button into the page, that can be styled accordingly.

When a user clicks on the button, a modal appears that contains an iframe with the contents of the provided URL.

The modal includes an icon that can be clicked to close the modal, and an option to close the modal when the user clicks outside of the modal content.

Sample modal window loading content from a local URL

Here’s an example that loads one of my blog posts into a modal:

 

It should be possible to load content from any external URL as well into this modal, as it just uses an iframe, but I haven’t thoroughly tested that implementation.

 

Parameters that can be passed into the modal shortcode

I created a number of parameters that can be passed into the shortcode to customise its behaviour:

  • modal_id – A unique value per page to allow more than modal to function correctly
  • modal_url – The content to be loaded into the modal window
  • modal_button – The text that the button for the modal should display
  • modal_heading – A heading that is displayed at the top of the modal window
  • modal_heading_tag – The HTML tag that surrounds the heading in the modal
  • modal_close_character – The HTML character that will be used as the close icon
  • modal_close_outside – Whether the modal can be closed by clicking outside of the modal content area
  • class – A custom class name to modify the look of the modal

Here’s the code I used to set up my shortcode function:

function nhs_display_product_form( $atts ){
$args = shortcode_atts( 
    array( 
        'modal_id' => '', 	    
        'modal_url' => '',
        'modal_button' => 'Buy now',
        'modal_heading' => '',		
        'modal_heading_tag' => 'h3',
        'modal_close_character' => '×',
        'modal_close_outside' => 'TRUE',		
        'class' => ''
        ), 
    $atts 
);
$modal_id = $args['modal_id'];
$modal_url = $args['modal_url'];
$modal_button = $args['modal_button'];
$modal_heading = $args['modal_heading'];	
$modal_heading_tag = $args['modal_heading_tag'];
$modal_close_character = $args['modal_close_character'];
$modal_close_outside = $args['modal_close_outside'];	
$class = $args['$class'];  

if( $modal_close_outside == 'FALSE' || $modal_close_outside == 'false' || $modal_close_outside == 'No' ){
    $modal_close_outside = FALSE;
}

Inserting more than one modal into a page

The standard modal code assumed there was only one modal in the page, and therefore the Javascript to open and close the modal would either have only worked for the first modal or not at all.

So I needed to abstract the code to be able to use a unique identifier in the variable names and element IDs, so that they would be unique within the page.

This meant a lot of abstraction and concatenation, using the unique modal_id, in the HTML and Javascript that was output to the page.

This also means there’s a couple of gotchas in the code:

  • The modal ID can only contain letters, numbers and underscores, not spaces or dashes
  • You have to use window.addEventListener to detect the button click, because event.target only picks up the first instance

Here’s the code I used to insert the HTML and Javascript into the page for each modal (the second half of my shortcode function):

ob_start();

/*** HTML ***/
/* Button */
echo '<button id="modal-button-' . $modal_id . '" class="button modal__button ' . $class . '">' . $modal_button . '</button>';

/* Modal */
echo '<div id="modal-' . $modal_id . '" class="modal__container' . $class . '">';
echo '<div id="modal-content-' . $modal_id . '" class="modal__content' . $class . '">';	
echo '<div class="modal__header">';	
echo '<span id="modal-close-' . $modal_id . '" class="modal__close">' . $modal_close_character . '</span>';      
if( strlen( $modal_heading ) > 0 ){
    echo '<' . $modal_heading_tag . ' class="modal__heading">' . $modal_heading . '</' . $modal_heading_tag . '>';
}
echo '</div>';    
if( strlen( $modal_url ) > 0 ){
    echo '<iframe id="modal-iframe-' . $modal_id . '" class="modal__iframe" width="100%" height="100%" src="' . $modal_url . '"></iframe>';
}    
echo '</div>';
echo '</div>';

/** Javascript **/
echo '<script type="text/javascript">' . "\n";
echo 'var m_' . $modal_id . '  = document.getElementById("modal-' . $modal_id . '");' . "\n";
echo 'var m_button_' . $modal_id . ' = document.getElementById("modal-button-' . $modal_id . '");' . "\n"; 
echo 'var m_close_' . $modal_id . ' = document.getElementById("modal-close-' . $modal_id . '");' . "\n";
//echo 'console.log(m_close_' . $modal_id . ');' . "\n";    
echo 'm_button_' . $modal_id . '.onclick = function() { m_' . $modal_id . '.style.display = "block"; }' . "\n";
echo 'm_close_' . $modal_id . '.onclick = function() { m_' . $modal_id . '.style.display = "none"; }' . "\n";
if( $modal_close_outside !== FALSE ){
echo 'window.addEventListener("click", function(event) { if ( event.target == m_' . $modal_id .' ) { m_' . $modal_id . '.style.display = "none"; } } )' . "\n"; 
}
echo '</script>' . "\n";    

$modal_code = ob_get_contents();
ob_end_clean();

return $modal_code;

}

And here’s the code I used to set up, register and document my shortcode:

/* SHORTCODE: Display a URL
* Usage 

* e.g. 

**/
/** Register shortcode **/
add_action( 'init', 'register_shortcode_display_product_form');

function register_shortcode_display_product_form(){
    add_shortcode('display-product-form', 'nhs_display_product_form');
}

And finally, here’s the CSS I used to style everything:

 /* The Modal (background) */
.modal__container {
  display: none; /* Hidden by default */
  position: fixed; /* Stay in place */
  z-index: 10; /* Sit on top */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}

/* Modal Content/Box */
.modal__content {
  background-color: #FFF;
  margin: clamp(1vh, 5vh, 10vh) auto; /* 10% from the top and centered */
  padding: clamp(2rem, 2.5rem, 3rem) clamp(2rem, 3rem, 4rem);
  border: 0px solid #888;
  border-radius: 0.5rem;
  width: 50vw; /* Could be more or less, depending on screen size */
  min-width: 25rem;
  max-width: 50rem;
  height: fit-content;
}
.modal__header{
   display: block;
}
.modal__heading{
    padding-top: 0.4rem !important;
    padding-bottom: 1rem !important;
    padding-left: 0.5rem;
}
.modal__content iframe{
    border: none;
    height: 63rem;
}

/* The Close Button */
.modal__close {
  color: #aaa;
  float: right;
  font-size: 32px;
  line-height: 36px;
  font-weight: bold;
}

.modal__close:hover,
.modal__close:focus {
  color: black;
  text-decoration: none;
  cursor: pointer;
}

 

Remaining challenges

This code is not a perfect solution for showing an external URL in a modal window after a button click, and there are a few remaining challenges that I may tackle at some point.

Displaying a link instead of a button

You could make this shortcode even more flexible by adding a modal_click_element attribute (or something like that) that takes either “button” or “link” as the value, and spits out different HTML code for the in-page element, so you could output a link to display the modal when clicked, instead of a button, as it currently does.

Pressing escape to close the modal

Many modal solutions also allow you to close the modal when someone presses the Escape key.

I haven’t explored this yet, but I imagine it would involve detecting the Escape button keypress, and using that to toggle the visibility of the modal via the same classes used by the close icon.

Fitting the iframe to the content

I played with various ways to get the iframe to resize itself to accommodate the content (vertically) without requiring a scroll bar.

Sadly, this is not a super straightforward exercise, and although there are some complex ways to tackle this, I tried a few, got close and then decided it just wasn’t worth the hassle.

I got to a point where it was adjusting the iframe height to suit the contents, but the results were inconsistent, and also varied across browsers and I ran out of patience for this exercise.

An acceptable alternative (to me anyway) is to use a custom class to set a fixed height of the iframe based on what I plan to display in it. Not perfect, but workable.

Here’s the javascript output I was playing with when I gave up, that was almost working, but not reliably:

/* modal height calculation - not reliable */
echo 'var m_iframe_' . $modal_id . ' = document.getElementById("modal-iframe-' . $modal_id . '");' . "\n";
echo 'm_iframe_' . $modal_id . '.onload = function(){' . "\n";
echo '   var m_iframe_height_' . $modal_id . ' = m_iframe_' . $modal_id . '.contentWindow.document.body.scrollHeight;' . "\n";
echo '   m_iframe_height_' . $modal_id . ' = m_iframe_height_' . $modal_id . ' + 100;' . "\n";
//echo '   m_iframe_' . $modal_id . '.height = m_iframe_' . $modal_id . '.contentWindow.document.body.scrollHeight + "px";' . "\n";
echo '   m_iframe_' . $modal_id . '.height = m_iframe_height_' . $modal_id . ' + "px";' . "\n";    
echo '   console.log(m_iframe_' . $modal_id . '.height);' . "\n";
echo '   console.log(m_iframe_height_' . $modal_id . ');' . "\n"; 
echo '}' . "\n";

Pre-filling the payment field

The last hurdle I encountered with this solution, after overcoming all of the other hurdles, is that the payment field in a WPForms is not pre-fillable for security reasons.

I understand why they’ve done this, but I was a bit disappointed to discover this after all that work, so now I have two options.

1. Create a different form for each product

This is what I was trying to avoid, so I could put all payments through a single form, but it would ensure that the right product is selected when the modal appears.

It also would allow me to use the WP Post Popup plugin, because the only thing it couldn’t handle was the parameters, which I no longer need if I’m using a different form for each product.

2. Create a different payment field for each product and show them conditionally

If I want to keep using a single form for all of my digital product transactions, then my only option is to create a different payment field for each different product, along with a separate dropdown of products, that I pre-fill using a URL parameter.

This value would then be used to conditionally show only the payment field for the currently selected product, so the customer is charged for the correct product.

This would allow me to keep using a single form, but it does mean setting up a new field every time I create a new product, and having to add new conditional logic each time.

This kind of messes with the structure of the WPForms form, because I’ll have the payment value in a different field each time, somewhat defeating the neat solution I had in mind, with different products going through the same form.

So I’m going to have to mull on this and decide whether to keep using my messy single form, or resign myself to creating a new form for every new product.

If I do revert, I may give up on my custom-rolled modal, but I’m kind of attached to it now.

Enjoy creating custom modals that load content from an external URL

So now you have a way to load content from a URL – either a local page or an external URL – into a modal that appears when someone clicks on a button.

And if you decide to use my shortcode approach, you’ll be able to add as many buttons as you like to a page, and also potentially expand and adapt the code to suit your needs.

I realise this is not a complete solution, but it works well enough for my purposes for now.

Let me know if you use this code and what else you manage to figure out!

And as always, please leave any questions or suggestions in the comments below.

Note: I am not available to provide individual support with implementing this code unless you wish to pay me a boatload of cash. Making it work for your site is up to you.

Nikki H Stokes

Nikki H Stokes

I’ve had a love affair with systems, technology and data for as long as I can remember. I’ve been building websites for over 20 years, running online businesses for more than 15, and teaching myself how to use gazillions of software programs since the very first moment I got my hands on a computer. I’m a geek and proud of it!

5 comments

  • Further to my comment above, I could not find details how to open URL as given example when you opened your blog post. Open the modal button or external URL, do you have simple codes for this.

  • Thank you so much that is what I was looking for to open a URL in popup. I tried so many popup plug in but mostly are PRO version to have URL. So this is what I am looking for. Let me try now. Thanks again.

  • Philippe

    great job nikki !

Leave your comment