diff --git a/README.md b/README.md
index 929dcc87a26c7ede6155d85e605f3facc70d9687..3068c03855ba8368ef6dfa391dfec0bdc5a5fb3f 100644
--- a/README.md
+++ b/README.md
@@ -317,7 +317,7 @@ Reveal.configure({
 
 When working on presentation with a lot of media or iframe content it's important to load lazily. Lazy loading means that reveal.js will only load content for the few slides nearest to the current slide. The number of slides that are preloaded is determined by the `viewDistance` configuration option.
 
-To enable lazy loading all you need to do is change your "src" attributes to "data-src" as shown below. This is supported for image, video, audio and iframe elements.
+To enable lazy loading all you need to do is change your "src" attributes to "data-src" as shown below. This is supported for image, video, audio and iframe elements. Lazy loaded iframes will also unload when the containing slide is no longer visible.
 
 ```html
 <section>
diff --git a/js/reveal.js b/js/reveal.js
index 79c8bbbb31e108635694b7feca48f7640ce8175c..ad99fdca7de5574d368749ab3e22f27b5c93faaf 100644
--- a/js/reveal.js
+++ b/js/reveal.js
@@ -2723,7 +2723,7 @@
 		slide.style.display = 'block';
 
 		// Media elements with data-src attributes
-		toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( function( element ) {
+		toArray( slide.querySelectorAll( 'img[data-src]', 'video[data-src]', 'audio[data-src]' ) ).forEach( function( element ) {
 			element.setAttribute( 'src', element.getAttribute( 'data-src' ) );
 			element.removeAttribute( 'data-src' );
 		} );
@@ -2909,19 +2909,24 @@
 				}
 			} );
 
-			// iframe embeds
+			// Lazy loading iframes
+			toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( element ) {
+				element.setAttribute( 'src', element.getAttribute( 'data-src' ) );
+			} );
+
+			// Generic postMessage API for non-lazy loaded iframes
 			toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
 				el.contentWindow.postMessage( 'slide:start', '*' );
 			});
 
-			// YouTube embeds
+			// YouTube postMessage API
 			toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
 				if( el.hasAttribute( 'data-autoplay' ) ) {
 					el.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' );
 				}
 			});
 
-			// Vimeo embeds
+			// Vimeo postMessage API
 			toArray( slide.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) {
 				if( el.hasAttribute( 'data-autoplay' ) ) {
 					el.contentWindow.postMessage( '{"method":"play"}', '*' );
@@ -2945,19 +2950,24 @@
 				}
 			} );
 
-			// iframe embeds
+			// Lazy loading iframes
+			toArray( slide.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( element ) {
+				element.removeAttribute( 'src' );
+			} );
+
+			// Generic postMessage API for non-lazy loaded iframes
 			toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
 				el.contentWindow.postMessage( 'slide:stop', '*' );
 			});
 
-			// YouTube embeds
+			// YouTube postMessage API
 			toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
 				if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
 					el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' );
 				}
 			});
 
-			// Vimeo embeds
+			// Vimeo postMessage API
 			toArray( slide.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) {
 				if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
 					el.contentWindow.postMessage( '{"method":"pause"}', '*' );
diff --git a/test/test.html b/test/test.html
index 29d02a9305dccf094968fde58a2f7b5f51f4167f..18f750401f66d36d435185396f7f9ad63a29bb3b 100644
--- a/test/test.html
+++ b/test/test.html
@@ -44,6 +44,7 @@
 							<li class="fragment">4.2</li>
 							<li class="fragment">4.3</li>
 						</ul>
+						<iframe data-src="http://example.com"></iframe>
 					</section>
 
 					<section>
diff --git a/test/test.js b/test/test.js
index 3f93d3c03a8a60e5dfe45bc540d07f63a935fa05..d59ddfa63bea319ba7d9ff3623a0731cb28db92d 100644
--- a/test/test.js
+++ b/test/test.js
@@ -495,6 +495,15 @@ Reveal.addEventListener( 'ready', function() {
 		strictEqual( document.querySelectorAll( '.reveal section img[src]' ).length, 1, 'Image source has been set' );
 	});
 
+	test( 'iframe with data-src', function() {
+		Reveal.slide( 0, 0 );
+		strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 0, 'Iframe source is not set' );
+		Reveal.slide( 2, 0 );
+		strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 1, 'Iframe source is set' );
+		Reveal.slide( 2, 1 );
+		strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 0, 'Iframe source is not set' );
+	});
+
 	test( 'background images', function() {
 		var imageSource1 = Reveal.getSlide( 0 ).getAttribute( 'data-background-image' );
 		var imageSource2 = Reveal.getSlide( 1, 0 ).getAttribute( 'data-background' );