title: Synchronize scrollbars of two JavaFx WebViews
body: <p>I'm using two <code>WebView</code>s to display two versions of HTML formatted text for comparison. The two display the same amount of text (same number of lines and corresponding lines have always the same length).</p>
  
  <p>When the displayed text exceeds the size of the node, the WebView gets scroll bars. Of course I want these scroll bars to scroll synchronously so that always the corresponding text is displayed.</p>
  
  <p>In order to supply a minimal, complete and verifiable example, I trimmed the code down to this:</p>
  
  <pre><code>import javafx.application.Application;
  import javafx.application.Platform;
  import javafx.geometry.Insets;
  import javafx.scene.Scene;
  import javafx.scene.control.ScrollBar;
  import javafx.scene.layout.GridPane;
  import javafx.scene.web.WebView;
  import javafx.stage.Stage;
  
  public class SynchronizedWebViewsTest extends Application {
  
    protected class DifferencePanel extends GridPane {
      private WebView actualPane;
      private WebView expectedPane;
  
      public DifferencePanel() {
        setPadding(new Insets(20, 20, 20, 20));
        actualPane = new WebView();
        expectedPane = new WebView();
        setResultPanes();
        addRow(0, actualPane, expectedPane);
      }
  
      public void setHtml(WebView webView) {
        Platform.runLater(() -&gt; {
          webView.getEngine().loadContent(createHtml());
        });
      }
  
      public void synchronizeScrolls() {
        final ScrollBar actualScrollBarV = (ScrollBar)actualPane.lookup(".scroll-bar:vertical");
        final ScrollBar expectedScrollBarV = (ScrollBar)expectedPane.lookup(".scroll-bar:vertical");
        actualScrollBarV.valueProperty().bindBidirectional(expectedScrollBarV.valueProperty());
        final ScrollBar actualScrollBarH = (ScrollBar)actualPane.lookup(".scroll-bar:horizontal");
        final ScrollBar expectedScrollBarH = (ScrollBar)expectedPane.lookup(".scroll-bar:horizontal");
        actualScrollBarH.valueProperty().bindBidirectional(expectedScrollBarH.valueProperty());
      }
  
      private String createHtml() {
        StringBuilder sb = new StringBuilder(1000000);
        for (int i = 0; i &lt; 100; i++) {
          sb.append(String.format("&lt;nobr&gt;%03d %2$s%2$s%2$s%2$s%2$s%2$s%2$s%2$s&lt;/nobr&gt;&lt;br/&gt;\n",
                                  Integer.valueOf(i), "Lorem ipsum dolor sit amet "));
        }
        return sb.toString();
      }
  
      private void setResultPanes() {
        setHtml(actualPane);
        setHtml(expectedPane);
      }
    } // ---------------------------- end of DifferencePanel ----------------------------
  
  
    public static void main(String[] args){
      launch(args);
    }
  
    @Override
    public void start(Stage dummy)  throws Exception {
      Stage stage = new Stage();
      stage.setTitle(this.getClass().getSimpleName());
      DifferencePanel differencePanel = new DifferencePanel();
      Scene scene = new Scene(differencePanel);
      stage.setScene(scene);
      differencePanel.synchronizeScrolls();
      stage.showAndWait();
    }
  }
  </code></pre>
  
  <p>I tried using adding a listener:</p>
  
  <pre><code>actualScrollBarV.onScrollFinishedProperty().addListener(event -&gt; {
    System.out.println(event);
  });
  </code></pre>
  
  <p>But the listener is never invoked.</p>
  
  <p>I'm using Java version 1.8.0_92, but with version 9.0.4 I get the same result.</p>
  
  <p>Can anybody tell me, what I'm missing here?</p>