I am using ports in elm to access the browser's fullscreen API. Everything works well in Chrome, but it does not work in Firefox. The error I get is: Request for full-screen was denied because Element.mozRequestFullScreen() was not called from inside a short running user-generated event handler.
I think I understand the error message, however, in a way I would expect it to work because I do access the fullscreen API via a button click. There is just an elm port in between. Has anyone solved this problem?
This is my elm code:
port module Main exposing (..)
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
import Html.Attributes exposing (class)
main =
Html.program { init = init, view = view, update = update, subscriptions = subscriptions }
-- Model
type alias Model =
{ fullscreen : Bool }
init : ( Model, Cmd Msg )
init =
( { fullscreen = False }
, Cmd.none
)
-- Ports
port activateFullscreen : String -> Cmd msg
port deactivateFullscreen : String -> Cmd msg
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
type Msg
= FullscreenMode Bool
-- Update
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
FullscreenMode on ->
let
m =
{ model | fullscreen = on }
in
if on then
( m, activateFullscreen "" )
else
( m, deactivateFullscreen "" )
-- views
fullScreenButton : Model -> Html Msg
fullScreenButton model =
case model.fullscreen of
False ->
button [ onClick (FullscreenMode True) ]
[ text "fullscreen on" ]
True ->
button [ onClick (FullscreenMode False) ]
[ text "fullscreen off" ]
view : Model -> Html Msg
view model =
div [ class "app" ] [ fullScreenButton model ]
and my html code:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Fullscreen Test</title>
</head>
<body>
<div id="main"></div>
<script src="main.js"></script>
<script>
(function () {
window.onload = function () {
var node = document.getElementById('main');
var app = Elm.Main.embed(node);
app.ports.activateFullscreen.subscribe( function () {
var element = document.querySelector('.app');
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
}
});
app.ports.deactivateFullscreen.subscribe( function () {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
});
};
}());
</script>
</body>
</html>
as bdukes's comment above points out, there is a workaround to this issue. Instead of using ports for the FullScreen handling, we can use functions that we put on the global window object. The resulting elm code for the button would be something like this:
button [ onClick (FullscreenMode True), attribute "onClick" "window.enterFullScreen()" ]
[ text "fullscreen on" ]
I would still keep elm's onClick
there to update the internal state.
In the JavaScript we can then define the enterFullScreen
function and handle our logic there:
window.enterFullScreen = function () {
var element = document.querySelector('.app');
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
}
};