JavaFX WebView addHyperlinkListener

July 30th, 2013 | by Tonny Kohar |

If you had use JTextPane HTML rendering mode, you can easily addHyperLinkListener to catch link click event and provide the handler. But there is no Hyperlink listener in JavaFX WebView.

So here how to do it in JavaFX WebView
* tested on Java 7u25

public class WebViewRenderer extends JFXPanel
    public static final String EVENT_TYPE_CLICK = "click";
    public static final String EVENT_TYPE_MOUSEOVER = "mouseover";
    public static final String EVENT_TYPE_MOUSEOUT = "mouseclick";
 
    protected WebView webView;
 
    public WebViewRenderer() {
        ...
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                initFX();
            }
        });
    }
 
    private void initFX() {
        createScene();
 
        webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
            @Override
            public void changed(ObservableValue ov, State oldState, State newState) {
                if (newState == Worker.State.SUCCEEDED) {
                    EventListener listener = new EventListener() {
                        @Override
                        public void handleEvent(Event ev) {
                            String domEventType = ev.getType();
                            //System.err.println("EventType: " + domEventType);
                            if (domEventType.equals(EVENT_TYPE_CLICK)) {
                                String href = ((Element)ev.getTarget()).getAttribute("href");
                                ////////////////////// 
                                // here do what you want with that clicked event 
                                // and the content of href 
                                //////////////////////                               
                            } 
                        }
                    };
 
                    Document doc = webView.getEngine().getDocument();
                    NodeList nodeList = doc.getElementsByTagName("a");
                    for (int i = 0; i < nodeList.getLength(); i++) {
                        ((EventTarget) nodeList.item(i)).addEventListener(EVENT_TYPE_CLICK, listener, false);
                        //((EventTarget) nodeList.item(i)).addEventListener(EVENT_TYPE_MOUSEOVER, listener, false);
                        //((EventTarget) nodeList.item(i)).addEventListener(EVENT_TYPE_MOUSEOVER, listener, false);
                    }
                }
            }
        });
    }
}

Thats it done. Now you can handle the hyperlink click event.

But if you really want to replicate addHyperlinkListener as in JTextPane just add the following

    @Override
    public void addHyperlinkListener(HyperlinkListener listener) {
        listenerList.add(HyperlinkListener.class, listener);
    }
 
    @Override
    public void removeHyperlinkListener(HyperlinkListener listener) {
        listenerList.remove(HyperlinkListener.class, listener);
    }
 
    protected void fireHyperlinkUpdate(EventType eventType, String desc) {
        HyperlinkEvent event = new HyperlinkEvent(this, eventType, null, desc);
 
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == HyperlinkListener.class) {
                ((HyperlinkListener) listeners[i + 1]).hyperlinkUpdate(event);
            }
        }
    }

And replace the above initFX with

    protected void initFX() {
        createScene();
 
        webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
            @Override
            public void changed(ObservableValue ov, State oldState, State newState) {
                if (newState == Worker.State.SUCCEEDED) {
                    EventListener listener = new EventListener() {
                        @Override
                        public void handleEvent(Event ev) {
                            String domEventType = ev.getType();
                            //System.err.println("EventType: " + domEventType);
                            final EventType eventType;
                            if (domEventType.equals(EVENT_TYPE_CLICK)) {
                                eventType = HyperlinkEvent.EventType.ACTIVATED;
                            } else if (domEventType.equals(EVENT_TYPE_MOUSEOVER)) {
                                eventType = HyperlinkEvent.EventType.ENTERED;
                            } else if (domEventType.equals(EVENT_TYPE_MOUSEOUT)) {
                                eventType = HyperlinkEvent.EventType.EXITED;
                            } else {
                                return;
                            }
 
                            final String href = ((Element)ev.getTarget()).getAttribute("href");
                            if (href == null) { return; }
                            SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    fireHyperlinkUpdate(eventType, href);
                                }
                            });
                        }
                    };
 
                    Document doc = webView.getEngine().getDocument();
                    NodeList nodeList = doc.getElementsByTagName("a");
                    for (int i = 0; i < nodeList.getLength(); i++) {
                        ((EventTarget) nodeList.item(i)).addEventListener(EVENT_TYPE_CLICK, listener, false);
                        ((EventTarget) nodeList.item(i)).addEventListener(EVENT_TYPE_MOUSEOVER, listener, false);
                        ((EventTarget) nodeList.item(i)).addEventListener(EVENT_TYPE_MOUSEOVER, listener, false);
                    }
                }
            }
        });
    }

So now you can easily use something like

WebViewRenderer renderer = new WebViewRenderer();
renderer.addHyperlinkListener(new HyperlinkListener() {
    @Override
    public void hyperlinkUpdate(HyperlinkEvent evt) {
        EventType eventType = evt.getEventType();
        String href = evt.getDescription();
        if (eventType.equals(HyperlinkEvent.EventType.ACTIVATED)) {
            // do something
        } else if (eventType.equals(HyperlinkEvent.EventType.ENTERED)) {
            // do something    
        } else if (eventType.equals(HyperlinkEvent.EventType.EXITED)) {
            // do something
        }
    }
});

Tags: , ,

  1. 4 Responses to “JavaFX WebView addHyperlinkListener”

  2. By Adam on Sep 30, 2013 | Reply

    I’m migrating to from JEditorPane to JavaFX and this is one of the things I’ve been looking for.
    Would it be possible to see the entire source? I get what you’ve posted here but some things are unclear, such as the collection used for listenerList.

    Many thanks.

  3. By Adam on Sep 30, 2013 | Reply

    😆 Oh nevermind. I was doing this in the wrong class and not the one that extended JFXPanel. Works really well. Please accept my thanks once more 🙂

  4. By vinay on Feb 18, 2014 | Reply

    thanks,really useful .Helped me alot.

  5. By Aleksandar on Feb 21, 2014 | Reply

    Hi. Is there any chance to see entire source? I have some problems with this. . .

You must be logged in to post a comment.