Loading web content

You can use WebView to load web content from the Internet in your app. Here's an overview of the classes that you need.


Diagram showing the WebView classes and their relationship.

To use the WebView APIs in your apps, you must add the access_internet permission to your bar-descriptor.xml file. To learn more about the bar-descriptor.xml file, see The bar-descriptor.xml file.

Loading a webpage

You can use WebView to load a webpage from the Internet. For example, you can build a simple search function that navigates to a search engine in a webpage. Here's how to load a webpage from the Internet by adding a WebView to a container in your app. If you want to try this, create a Cascades project using the Standard empty project template and use the following code sample:

Container {
    id: mainContr
    WebView {
       id: webView
       url: "<search_engine_URL>"
    }
}// end Container
Container* mainContr = new Container();

WebView* webView = new WebView();
webView->setUrl(QUrl("<search_engine_URL>"));

mainContr->add(webView);

Not applicable

In the code sample above, the WebView isn't in a scrollable container, so the webpage displayed in your app is restricted by the size of your container. WebView itself doesn't have a size. If a WebView is added to a Container, it adapts to the size of the web content loaded. The exact layout of the WebView can be customized using properties of the container. For more information, see Layouts. To make the WebView more useful, you can use ScrollView to let the user scroll forward and backward on a webpage.

Here's a code sample that adds a WebView to a ScrollView within a container.

import bb.cascades 1.4

Page {
    ScrollView {
        Container {
            background: Color.create ("#f8f8f8")
            layout: StackLayout {
                orientation: LayoutOrientation.TopToBottom
            }
            WebView {
                url: "<search_engine_URL>"
            }
        }
    }// end ScrollView
}// end Container
// Create a Container object and set its properties
Container* scrlvwContainer = Container::create()
    .background(Color::fromRGBA(248,248,248,1));
StackLayout* stkLyout = new StackLayout();
stkLyout->setOrientation(LayoutOrientation::TopToBottom);
scrlvwContainer->setLayout(stkLyout);

// Create a WebView object
WebView* webview = new WebView();
webview->setUrl(QUrl("<search_engine_URL>"));

// Add the webview object to the container
scrlvwContainer->add(webview);

// Create a ScrollView container and set
// the scrlvwContainer as its content
ScrollView* scrollView = ScrollView::create()
    .content(scrlvwContainer);

Not applicable

If you create this UI in a NavigationPane with a TextField for input and a Button, you can build a search function into your app. You can build this example with two files: the main.qml file, which contains the UI for a search button in your container, and the search.qml file, which creates the searching function using a WebView and a search engine. This example uses Google but you can use any search engine. Here's how you can create the UI for the search button in main.qml:

NavigationPane {
    id: navPane

    Page {
        id: page

        Container {
            id: mainContr

            layout: StackLayout {
                orientation: LayoutOrientation.LeftToRight
            }
            TextField {
                id: srchTerm
            }
            Button {
                id: srchButton
                text: "Search"
    
                onClicked: {
                    var page = componentDef.createObject();
                    navPane.push(page);
               }
    
               attachedObjects: ComponentDefinition {
                    id: componentDef
                    source: "mysearch.qml"
               } // end attachedObjects
            } // end Button
        } // end Container
    } // end Page
} // end NavigationPane

Not applicable

Not applicable

In the mysearch.qml file, you can use a WebView to display a web search with the text that the user enters in the TextField that is part of the UI that is created in main.qml.

Here's a code sample that uses a TextField, called srchTerm from the UI in main.qml, to perform a search on Google:

import bb.cascades 1.4

NavigationPane{
    Page {
        ScrollView {
            Container {
                id: mainContr

                background: Color.create ("#f8f8f8")
                layout: StackLayout {
                   orientation: LayoutOrientation.TopToBottom
                }
    
                WebView {
                     url: "http://www.google.com/search?q=" + srchTerm.text
                } // end WebView    
            } // end Container
        } // end ScrollView
    } // end Page
}// end NavigationPane    
Container* mainContr = new Container();
mainContr->setBackground(Color::fromRGBA(248, 248, 248, 1));

StackLayout* stkLyout = new StackLayout();
stkLyout->setOrientation(LayoutOrientation::TopToBottom);
mainContr->setLayout(stkLyout);

WebView* webView = new WebView();
QString srchStr = "http://www.google.com/search?q=";
srchStr.append(srchTerm->text());
webView->setUrl(QUrl(srchStr));

mainContr->add(webView);

ScrollView* scrollview = ScrollView::create()
    .content(mainContr);

// Create a NavigationPane and add (push)
// the page to it
NavigationPane* navPane = NavigationPane::create()
    .add(Page::create().content(scrollview));

Not applicable

Using relative height in web content

Web content that uses relative height (such as <body style="height: 100%">) uses the minHeight or preferredHeight properties of the WebView. Hard-coding a height can be problematic, considering the varying screen sizes of BlackBerry 10 devices.

Instead of using relative height, you can create a LayoutUpdateHandler for the Container the contains the WebView. Then you can use the LayoutFrameChanged signal to set the minHeight or preferredHeight of the WebView to the value of the layoutFrame.height.

Here's a code sample that shows you how to create a LayoutUpdateHandler to make sure that your web content uses all available space.

import bb.cascades 1.4

Page {
    Container {
        ScrollView {
            scrollViewProperties {
                scrollMode: ScrollMode.Both
            }
            WebView {
                id: webView
                html: "<body style=\"height: 100%\"><p>Sample HTML code with relative height on the body tag</p></body>"
            }
        }
        attachedObjects: LayoutUpdateHandler {
            onLayoutFrameChanged: {
                webView.preferredHeight = layoutFrame.height;
            }
        }
    }// end Container
}// end Page
//...

webView = new WebView();
webView->setHtml("<body style=\"height: 100%\"><p>Sample HTML code with relative height on the body tag</p></body>");

scrollView = ScrollView::create()
            .scrollMode(ScrollMode::Both)
            .content(webView);

LayoutUpdateHandler::create(scrolVwContr)
    .onLayoutFrameChanged(this, SLOT(handleLayoutFrameUpdated(QRectF)));

scrolVwContr = new Container();
scrolVwContr->add(scrollView);
mainContr = root->findChild<Container*>("mainContainer");
mainContr->add(scrolVwContr);

//...

Not applicable

Scaling a webpage

Each WebView object has an associated WebSettings object that you can use to improve the readability of your WebView. You can set font sizes, adjust zoom settings, or scale content using a ratio between device pixels (or physical pixels) and device-independent pixels (or CSS pixels). CSS pixels are not the same as device pixels. CSS pixels expand and contract with zooming, and the ratio of device pixels to CSS pixels depends on the scale factor used to display the web content on the device's screen. CSS pixels equal device pixels when content is scaled to 100%.

Some best practices for displaying web content in a WebView are described below.

Consider font sizes. You can set font sizes using the defaultFontSize, minimumFontSize, and defaultFontSizeFollowsSystemFontSize properties. The default font size that a WebView uses is 16 CSS pixels and the default minimum font size is 8 CSS pixels.

If you set the defaultFontSizeFollowsSystemFontSize property to true, the defaultFontSize is ignored and the default system font size is used. You can set the defaultFontSizeFollowsSystemFontSize to true, combined with relative font sizes in your content, to make an accessible WebView whose text scales with the system font size when it changes.

For more information about setting and querying font properties in WebView, see the API Reference.

Use default settings. If you don't set any properties to adjust the scaling of your WebView, your web content fills the device screen so that you can see all of the content on one page. By default, the zoomToFitEnabled property ensures that your content fills the WebView on the screen when it is loaded.

When no viewport is specified and the defaultFontSizeFollowsSystemFontSize property defaults to false, the webpage starts out with an appropriate scale for the BlackBerry 10 device screen. These defaults also indicate that the default font size does not change when users change their font size settings.

Screen showing a WebView with no scaling properties set.

Here's a code sample that contains a WebView. The image above illustrates how the corresponding content appears on the device.

WebView {
    id: webView
    url: "http://developer.blackberry.com"
}
WebView* webView = new WebView();
webView->setUrl(QUrl("http://developer.blackberry.com"));

Not applicable

There are two approaches to scaling your web content if the default settings don't display your web content appropriately.

  • You can use a devicePixelRatio of 1.0 and tailor your web content to fit the standard screen width of BlackBerry 10 devices. Typically, this setting is used when the web content is created specifically for BlackBerry 10 devices.

  • You can use the default devicePixelRatio and use a viewport value of "initial-scale: 1.0" for web content that it is designed to be compatible with a wide range of mobile devices. Typically, this setting is used when the web content is an existing mobile webpage.

As a best practice, you should determine the display dimensions of the device that you are running your app on. Then, you can create a UI layout that adapts to different screen sizes and resolutions. For more information about designing your app for different screen resolutions, see Resolution independence.

Use a device pixel ratio of 1.0. You can use the devicePixelRatio to adjust the zoom level of the web content in your WebView. Increasing devicePixelRatio zooms in the content and decreasing it zooms out the content.

If you set the device pixel ratio to 1.0, your web content should be designed to fit the width of a BlackBerry 10 device in portrait orientation (720 CSS pixels), but it should use extra space if it is available.

Screen showing a WebView with a pixel ratio of 1.0.

Here's a code sample that demonstrates a device pixel ratio of 1.0. in a WebView. The image above illustrates how the corresponding content appears on the device

WebView {
    id: webView
    url: "http://developer.blackberry.com"
    settings.devicePixelRatio: 1.0
}
WebView* webView = new WebView();
webView->setUrl(QUrl("http://developer.blackberry.com"));
webView->settings()->setDevicePixelRatio(1.0);

Not applicable

In the example above, the devicePixelRatio setting indicates that one CSS pixel is equal to one device pixel. This setting is useful for web content tailored to BlackBerry 10 devices, because having one CSS pixel equal one device pixel can simplify the design process.

Here's a code sample that demonstrates setting the pixel ratio to reduce the size of the web content in a WebView.

WebView {
   id: webView
   url: "http://developer.blackberry.com"
   settings.devicePixelRatio: 0.5
}

Notice that the WebView still fills the device screen, but the web content has been scaled to half of its original size.

Screen showing a WebView with device pixel ratio 0.5.

This example, and the effect it has on the output, is shown here for demonstration only. In this example, the web content is not readable. You would not display this type of web content using this pixel ratio.

Use the default device pixel ratio and the viewport property. You can use the viewport property to scale the content of your WebView so that it fills the width of the device and is zoomed in for readability.

Here's a code sample that contains a WebView that sets the viewport property. The code sample uses the default devicePixelRatio. This default value depends on which BlackBerry 10device you are running your app on. Your web content should set the viewport property to "initial-scale=1.0".

The image to the right illustrates how the corresponding content appears on the device.

Screen showing a WebView with viewport scaling properties set.
WebView {
    id: webView
    url: "http://developer.blackberry.com"
    settings.viewport: {
        "initial-scale": 1.0
    }
}
WebView* webView = new WebView();
webView->setUrl(QUrl("http://developer.blackberry.com"));
webView->settings()->setDevicePixelRatio(1.0);
QVariantMap map;
    map["initial-scale"] = "1.0";

webView->settings()->setViewportArguments(map);

Not applicable

Because this strategy is used when the web content is designed to be compatible with a wide range of mobile devices, it should be designed to fit in a width of 320 CSS pixels, but use extra space if it is available.

With the default device pixel ratio setting, one CSS pixel typically equals two or more device pixels and the exact value can be a fraction. For example, on BlackBerry 10 devices with a 768 x 1280 pixel screen, the default devicePixelRatio is 2.243750.

The key-value pair, "initial-scale: 1.0", zooms in on the content for readability. The initial-scale value controls the zoom level when the page is first loaded. You can also use maximum-scaleminimum-scale, and user-scalable values to control how users are allowed to zoom on the page. For more details about how to allow users to zoom on a webpage, see Adding gesture handling to your WebView.

Monitoring webpage progress

WebView signals are emitted when anything related to your webpage changes. When you display a webpage in your app, you can use signals to monitor the progress of your webpage loading or determine when the user navigates away from the initial webpage using a link. For more information about handling signals, see Signals and slots.

You can use the loadingChanged() signal and the WebLoadRequest to determine the status of a webpage load request. Here's a code sample that demonstrates this technique:

Label {
  id: statusLabel
  leftMargin: 10
  text: "No webpage yet."
} 
WebView {
  id: webView
  url: "http://developer.blackberry.com"

  onLoadingChanged: {
        if (loadRequest.status ==  WebLoadStatus.Started ) {
           statusLabel.setText("Load started.")
        }
        else if (loadRequest.status ==  WebLoadStatus.Succeeded ) {
           statusLabel.setText("Load finished.")
        }
        else if (loadRequest.status ==  WebLoadStatus.Failed ) {
              statusLabel.setText("Load failed.")
        }
    }
}
// Global variable
bb::cascades::Label* statusLabel;

//...

statusLabel = Label::create()
    .leftMargin(10)
    .text("No webpage yet.");

WebView* webView = new WebView();
webView->setUrl(QUrl("http://developer.blackberry.com"));
result = QObject::connect(webView,
        SIGNAL(loadingChanged (bb::cascades::WebLoadRequest*)),
        this,
        SLOT(onLoadingChanged (bb::cascades::WebLoadRequest*)));
Q_ASSERT(result);

//...

Here's the onLoadingChanged() slot, which is used to handle the loadingChanged() signal:

// This slot displays the current load status
void ApplicationUI::onLoadingChanged(bb::cascades::WebLoadRequest* loadRequest)
{
    if (loadRequest->status() ==  WebLoadStatus::Started) {
        statusLabel->setText("Load started.");
    }
    else if (loadRequest->status() ==  WebLoadStatus::Succeeded ) {
        statusLabel->setText("Load finished.");
    }
    else if (loadRequest->status() ==  WebLoadStatus::Failed ) {
        statusLabel->setText("Load failed.");
    }
}

Not applicable

Handling navigation on a webpage

You can use the navigationRequested() signal and the WebNavigationRequest properties to handle user interaction and navigation on a webpage that you displayed using WebView. The navigationRequested() signal is emitted when the user tries to navigate to a new URL from the WebView that's displayed in your app.

Here's a code sample that shows you how to use the navigationRequested() signal:

Label {
  id: statusLabel
  text: "No navigation request yet."
}
WebView {
  id: webView
  url: "http://developer.blackberry.com"

  onNavigationRequested: {
     statusLabel.setText("Requested URL = " + 
            request.url + 
            " navigationType = " +
            request.navigationType);
  } 
}
// Global variable
bb::cascades::Label* statusLabel;

//...

statusLabel = Label::create()
    .text("No navigation request yet.");

WebView* webView = new WebView();
webView->setUrl(QUrl("http://developer.blackberry.com"));
result = QObject::connect(webView,
        SIGNAL(navigationRequested (bb::cascades::WebNavigationRequest*)),
        this,
        SLOT(onNavigationRequested (bb::cascades::WebNavigationRequest*)));
Q_ASSERT(result);

//...

Here's the onNavigationRequested() slot, which is used to handle the navigationRequested() signal:

// This slot displays the current navigation request status
void ApplicationUI::onNavigationRequested(bb::cascades::WebNavigationRequest* navRequest)
{
    QString statusStr = "Requested URL = ";
    statusStr.append(navRequest->url().toString());
    statusStr.append(" navigationType = ");
    statusStr.append(navRequest->navigationType());

    statusLabel->setText(statusStr);
}

Not applicable

You can restrict whether the user has permission to change the URL of the content in a WebView. When the URL of the webpage is changed, the navigationRequested signal is emitted. You can use the WebNavigationRequest to determine what type of navigation was requested by the user and the URL that the user wants to navigate to. You can reject the navigation by changing the action property of the WebNavigationRequest or by calling ignore().

Here's a code sample that shows you how to use the ignore() function:

Label {
  id: statusLabel
  text: "No navigation request yet."
}
WebView {
  id: webView
  url: "http://developer.blackberry.com"

  onNavigationRequested: {
     statusLabel.setText("Requested URL = " + 
            request.url + 
            " navigationType = " +
            request.navigationType);

     if (!acceptNavToUrl(request.url)) {
         request.action = WebNavigationRequestAction.Ignore 
     }
  }
}
// Global variable
bb::cascades::Label* statusLabel;

//...

statusLabel = Label::create()
    .text("No navigation request yet.");

WebView* webView = new WebView();
webView->setUrl(QUrl("http://developer.blackberry.com"));
result = QObject::connect(webView,
        SIGNAL(navigationRequested (bb::cascades::WebNavigationRequest*)),
        this,
        SLOT(onNavigationRequested (bb::cascades::WebNavigationRequest*)));
Q_ASSERT(result);

//...

An if statement and our custom helper function, called acceptNavToUrl(), helps the code sample determine whether or not to use the ignore() function in its onNavigationRequested() slot:

// This slot displays the current navigation request status
void ApplicationUI::onNavigationRequested(bb::cascades::WebNavigationRequest* navRequest)
{
    QString statusStr = "Requested URL = ";
    statusStr.append(navRequest->url().toString());
    statusStr.append(" navigationType = ");
    statusStr.append(navRequest->navigationType());

    statusLabel->setText(statusStr);

    if (acceptNavToUrl(navRequest->url()) == false)
    {
        navRequest->setAction(WebNavigationRequestAction::Ignore);
    }
}

Not applicable

By default, navigation requests are accepted and the new URL is loaded into the WebView. When you want navigation requests to open in a new tab or window, your app must reject the navigation request and open the requested URL in a new WebView. Navigation requests are delegated to your app so that your app can accept or ignore the navigation. If a user navigates to a webpage that involves HTTP redirects, several navigation requests will be made for each URL involved. Your app can obtain the target of a redirect and accept or ignore each navigation request that the user makes.

A WebView is not intended to be used as a full web browser for viewing web content. However, a WebView has a webpage history mechanism that you can use to track the user's navigation history. To track the user's navigation history, you can use the canGoBack() property and the goBack() function to navigate through the user's previous requests.

Here's a code sample that shows you how to use the canGoBack() property and the goBack() function:

import bb.cascades 1.4

Page {
    content: Container {        
        // Set up a Container with three buttons to
        // use to go through the webpage history
        Container {
            Button {
                id: backButton
                text: "Back"
                onClicked: { webView.goBack(); }
            }
            Button {
                id: forwardButton
                text: "Forward"
                onClicked: { webView.goForward(); }
            }
            Button {
                id: refreshButton
                text: "Refresh"
                onClicked: { webView.reload(); }
            }
        }

        // Set up a Container and a ScrollView to
        // display a webpage in a WebView
        Container {
            ScrollView {
                scrollViewProperties.pinchToZoomEnabled: true
                scrollViewProperties.scrollMode: ScrollMode.Both
                
                WebView {
                    id: webView
                    url: "http://<mySiteURL>/"
                }
            }
        }
    }
}
// Set up a Container with three buttons to
// use to go through the webpage history
Container* btnContr = new Container();

Button* backButton = Button::create()
    .text("Back")
    .onClicked(this, SLOT(onGoBackClick()));

Button* forwardButton = Button::create()
    .text("Forward")
    .onClicked(this, SLOT(onGoForwardClick()));

Button* refreshButton = Button::create()
    .text("Refresh")
    .onClicked(this, SLOT(onRefreshClick()));

btnContr->add(backButton);
btnContr->add(forwardButton);
btnContr->add(refreshButton);

// Set up a Container and a ScrollView to
// display a webpage in a WebView
Container* scvwContr = new Container();
ScrollView* scrollview = ScrollView::create()
    .pinchToZoomEnabled(true)
    .scrollMode(ScrollMode::Both);
WebView* webView = new WebView();
webView->setUrl(QUrl("http://<mySiteURL>"));

scrollview->setContent(webView);
scvwContr->add(scrollview);

Here are the three slots that handle each of the three buttons' click event:

void ApplicationUI::onGoBackClick()
{
    webView->goBack();
}

void ApplicationUI::onGoForwardClick()
{
    webView->goForward();
}

void ApplicationUI::onRefreshClick()
{
    webView->reload();
}

Not applicable

If you need to view web content in a full browser, you can use the invocation framework to invoke the BlackBerry Browser. For more information about invoking BlackBerry Browser, see App integration.

Searching for text on a webpage

To help the user find text on a webpage, you can use the findText() function. By default, each instance of the text that the user wants to find is highlighted from the top of the page to the bottom. When the search reaches the bottom of the webpage, it wraps and continues at the top of the webpage. By default, searches are not case-sensitive.

You can use the WebFindFlag class to specify whether a webpage is searched from top to bottom (by default) or from bottom to top, whether the search is case-sensitive, whether it wraps around, or whether all instances of the search term are highlighted on the webpage or whether each term found on the page is highlighted sequentially.

Screen showing a simple function to find text on a WebView.

Here's a code sample that adds a TextField and a Button to a Container. The Container also includes a scrolling WebView that contains web content.

//...

Container {
    id: mainContr
    
    Container {
        id: scrlvwContr
        
        TextField {
            id: jsField
            hinttext: "Search for ..."
        }
        
        Button {
            id: findButton
            text: "Find on page"
            onClicked: { webView.findText(jsField.text) }
        }
    }// end scrlvwContr

    ScrollView {
        scrollViewProperties {
            scrollMode: ScrollMode.Both
        }

        Container {
            id: wbvwContr

            WebView {
                id: webView
                url: "http://developer.blackberry.com/"
            }
         } // end wbvwContr        
     } // end ScrollView
 } // end mainContr
 
 //...
// Global variable
bb::cascades::WebView* webView;

//...

// In the app constructor

Container* mainContr = new Container();
Container* scrlvwContr = new Container();
Container* wbvwContr = new Container();

webView = new WebView();
webView->setUrl(QUrl("http://developer.blackberry.com/"));

ScrollView* scrollView = ScrollView::create()
    .scrollMode(ScrollMode::Both);

TextField* jsField = TextField::create()
    .hintText("Search for ...");

Button* findButton = Button::create()
    .text("Find on page")
    .onClicked(this, SLOT(onFindClicked()));

scrlvwContr->add(jsField);
scrlvwContr->add(findButton);    
mainContr->add(scrlvwContr);

wbvwContr->add(webView);
scrollView->setContent(wbvwContr);
mainContr->add(scrollView);

//...

Here's the onFindClicked() slot:

void ApplicationUI::onFindClicked()
{
    webView->findText(jsField->text());
}

Not applicable

Last modified: 2015-07-24



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus