Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Form in iframe unable to regain focus with Angular and IE11

I have an Angular app with a route that contains an iframe. The iframe loads a form. On the first load of the route, you are able to enter data into the form. On the second load of the route the form input is unable to regain focus on click in IE11. Works in Chrome and Firefox.

The specific steps to recreate are as follows:

  1. Load page in IE11
  2. Click Iframe link
  3. Data can be entered into form input
  4. While the cursor is still in the form input, click the Iframe link
  5. The route will reload, clearing the form
  6. When clicking the form input to enter new text, the form is unable to regain focus

I noticed that if you click the Home link after entering text, then click the iframe link the form is able to regain focus allowing new text to be entered.

Below is sample code that recreates the issue. You can see it running here: http://matthewriley.github.io/iframe-form/.

Note that the controller forces a reload of the route based on a flag. This is to support a business requirement that if a user is in the iframe and choses to click the iframe link, the route will fully reload.

What causes IE11 to prevent the form in the iframe from regaining focus on the second load?

HTML Page

<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta charset="utf-8" />
        <title>Iframe Reload Test</title>
    </head>
    <body>
        <div ng-app="IframeTest">
            <p><span><a href="#/">Home</a></span>&nbsp;<span><a href="#/iframe">Iframe</a></span></p>
            <div ui-view></div>
        </div>
        <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular.min.js"></script>
        <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script>
        <script src="app.js"></script>
    </body>
</html>

Angular App

// app.routes.js
(function() {
    'use strict';
    angular
        .module('IframeTest', [
            'ui.router',
            'IframeTest.iFrameModule'
        ])
        .config(routeConfig);
    routeConfig.$inject = ['$stateProvider', '$urlRouterProvider'];
    function routeConfig($stateProvider, $urlRouterProvider) {
        $urlRouterProvider.otherwise("/");
        $stateProvider
            .state('home', {
                url:'/',
                template: '<h4>Home</h4><p>This is the home page.</p>'
            })
            .state('iFrame', {
                url:'/iframe',
                template: '',
                controller: 'IFrameCtrlAs',
                controllerAs: 'vm'
            })
            .state('iFrame/:flag', {
                url:'/iframe/:flag',
                template: '<h4>Iframe</h4><p><iframe src="{{vm.url}}" id="iframeID" frameborder="1" width="100%" scrolling="no"></iframe></p>',
                controller: 'IFrameCtrlAs',
                controllerAs: 'vm'
            });
    }
})();

// iFrame.controller.js
(function() {
    'use strict';
    angular
        .module('IframeTest.iFrameModule', [])
        .controller('IFrameCtrlAs', IFrameCtrlAs);
    IFrameCtrlAs.$inject = ['$stateParams', '$location'];
    function IFrameCtrlAs($stateParams, $location) {
        var vm = this;
        if(!angular.isDefined($stateParams.flag)){
            var reloadPath = $location.path() + '/true';
            $location.path(reloadPath);
        }
        else{
            vm.url = 'form.html';
        }
    }
})();

Form in Iframe

<form method="post" action="">
    <input type="text" id="example1" name="example1" placeholder="sample text">
</form>
like image 839
Matt Riley Avatar asked Aug 14 '15 16:08

Matt Riley


1 Answers

I have no idea what causes this issue, probably IE quirk. Anyway, I found that it works just fine if you focus the iframe manually in JS but it must be in a $timeout.

I added an ng-init to the iframe that basically focuses the iframe manually.

.state('iFrame/:flag', {
        url:'/iframe/:flag',
        template: '<h4>Iframe</h4><p><iframe src="{{vm.url}}" ng-init="vm.init()" id="iframeID" frameborder="1" width="100%" scrolling="no"></iframe></p>',
        controller: 'IFrameCtrlAs',
        controllerAs: 'vm'
    });

And here's the controller

function IFrameCtrlAs($stateParams, $location, $timeout) {
    var vm = this;

    if(!angular.isDefined($stateParams.flag)){
        var reloadPath = $location.path() + '/true';
        $location.path(reloadPath);
    }
    else{
        vm.url = 'form.html';

    }
    vm.init = function(){
            $timeout(function(){        
                document.getElementById("iframeID").contentWindow.focus();
                console.log("loaded")
            })
    }
}

Of course, it might be cleaner to wrap this in a directive.

like image 198
gafi Avatar answered Sep 18 '22 08:09

gafi