eSKape eSKape - 2 months ago 16
Java Question

HTML Element Selector with Java & Vaadin

I'm currently building a Vaadin Webapp for getting the path of certain HTML-Elements from a website. Therefore, i want to


  1. load a webpage inside an IFrame / BrowserFrame of a Vaadin Page

  2. let the user visually click on an highlighted HTML-Element (in a way like http://selectorgadget.com/)

  3. generate the Path (XPath seems to be the best choice) and

  4. display it inside a formular of the Vaadin Page (on the same site, as to check the selections)



My questions:


  1. Is the transfer of the path data from the website inside the iFrame to the Vaadin App actually possible?

  2. Is the selection of html elements possible with techniques provided by the Vaadin App (so that i do not need any Javascript inside the IFrame)?

  3. Is there some technology other than iFrames to better accomplish my goal?



Thanks in advance!

Answer

I've managed to get a solution: Full interaction between Javascript inside the iframe and the VaadinUI (As the component to build has to be integrated in an existing VaadinUI) is possbile by:

1. Injecting Scripts, css and other needed Html Content into the loaded Webpage. The selection scripts of the solution were taken and extended from here

var elements = {
  top: $('#selector-top'),
  left: $('#selector-left'),
  right: $('#selector-right'),
  bottom: $('#selector-bottom')
};

function registerMouseMove() {
  $(document).mousemove(function(event) {
    if (event.target.id.indexOf('selector') !== -1 || event.target.tagName === 'BODY' || event.target.tagName === 'HTML') return;

    var $target = $(event.target);
    targetOffset = $target[0].getBoundingClientRect(),
      targetHeight = targetOffset.height,
      targetWidth = targetOffset.width;
    //console.log(targetOffset);

    elements.top.css({
      left: (targetOffset.left - 4),
      top: (targetOffset.top - 4),
      width: (targetWidth + 5)
    });
    elements.bottom.css({
      top: (targetOffset.top + targetHeight + 1),
      left: (targetOffset.left - 3),
      width: (targetWidth + 4)
    });
    elements.left.css({
      left: (targetOffset.left - 5),
      top: (targetOffset.top - 4),
      height: (targetHeight + 8)
    });
    elements.right.css({
      left: (targetOffset.left + targetWidth + 1),
      top: (targetOffset.top - 4),
      height: (targetHeight + 8)
    });

  });
};

function unregisterMouseMove() {
  $(document).unbind();
  elements.top.css({
    left: 0,
    top: 0,
    width: 0
  });
  elements.bottom.css({
    top: 0,
    left: 0,
    width: 0
  });
  elements.left.css({
    left: 0,
    top: 0,
    height: 0
  });
  elements.right.css({
    left: 0,
    top: 0,
    height: 0
  });
};

function registerClickListener() {

  $(document).click(function(event) {
    if (event.target.id.indexOf('selector') !== -1 || event.target.tagName === 'BODY' || event.target.tagName === 'HTML')
      return;

    var target = event.target;

    var xpath = Xpath.getElementXPath(target);

    $.ajax({
      type: "POST",
      url: "/rest?text=" + xpath //,
    });

    console.log(xpath);

  });
};

function unregisterClickListener() {
  $(document).off("click");
}

function disableLinks() {
  $('a').click(function(event) {
    event.preventDefault();
  });
}

function enableLinks() {
  $('a').unbind('click');
}

function startSelecting() {
  registerMouseMove();
  registerClickListener();
  disableLinks();
}

function stopSelecting() {
  unregisterClickListener();
  unregisterMouseMove();
  enableLinks();
}

<!-- only for demonstration, delete! for use in programm call startSelecting by a Javascript call from the UI -->
window.onload = function(event) {
  startSelecting();
};
#selector-top,
#selector-bottom {
  background: blue;
  height: 3px;
  position: fixed;
  transition: all 300ms ease;
}
#selector-left,
#selector-right {
  background: blue;
  width: 3px;
  position: fixed;
  transition: all 300ms ease;
}
<div id="selector">
  <div id="selector-top"></div>
  <div id="selector-left"></div>
  <div id="selector-right"></div>
  <div id="selector-bottom"></div>
</div>

<html>

<head>
  <script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js " /></script>
  <script type="text/javascript" src="link/to/xpath.js"></script>
</head>

<body>
  <span><b>Test</b></span>
  <p>trallalalala</p>
</body>

</html>

2. Calling defined Javascripts inside the iframe using Vaadins Javascript calls (see here) and some Javascript path magic: Page.getCurrent().getJavaScript().execute("window.frames[1].frameElement.contentWindow.startSelecting();");

  1. Use Firebugs xpath.js script to get the XPath when clicking on an item:

var xpath = Xpath.getElementXPath(event.target);

4. Use a ajax rest call to post data to your Vaadin Application:

$.ajax({
   type: "POST",
   url: "/rest?text="+xpath
});

5. Create a rest interface, thats using a Vaadin Broadcaster to broadcast the xpath value to the UI. For details on how to use the Broadcaster and Push for VaadinUI, see Eric G. post here

``@RequestMapping("/rest")
public String sayHello(@RequestParam(value = "text", required = true) String text) {
    Broadcaster.broadcast(text);
}``

6. Finally: The VaadinUI receives the Broadcast and prints the xpath e.g. to a TextField and processes it further

The only downside of this is, that it can be only used for single user applications, as i not managed to differentiate between different instances of the same UI, i.e. one rest call will start a broadcast to all the instances of the UI and transport the same data to them.

I hope this helps future visitors. :)

Comments