5

I am trying to resize the container div when the Recaptcha displays the puzzle instead of the checkbox. This usually happens after the checkbox is clicked. How can I do this?

I know there are certain conditions that will force Recaptcha V2 to display a puzzle instead of a checkbox. The Api however does not return a property or event that would indicate that this change has happened. Is there a way in JavaScript or via the API i can identify whether or not the Recaptcha V2 is displaying the puzzle?

Note: that this would be in the case where I haven't specified fallback=true in the API url.

Shayan Zafar
  • 113
  • 1
  • 13
  • Does it open the Recaptcha in an iframe? Are you able to do a check to see if something with those classes exists on the page? – krillgar Sep 08 '16 at 17:27
  • They add HTML to your page. You would need to poll your page and look for this class name `g-recaptcha-bubble-arrow` - it may help to look for mutations: http://stackoverflow.com/questions/3219758/detect-changes-in-the-dom#answer-3219767 – colecmc Sep 08 '16 at 18:07
  • 1
    it does open in the iframe. I have looked at some of the elements and their ids generated but I have no luck in targeting these css selectors with DOM manipulation – Shayan Zafar Sep 08 '16 at 18:46
  • Since you can't access the elements within the iframe, I think the best bet you're going to have is to check the size of the iframe before and after you think it should be called. However, with the fact that you're trying to determine if it is a puzzle or checkbox, any code you write will be very flimsy. – krillgar Sep 08 '16 at 18:57
  • I feel as though the API should provide some property or hook when it decides to render a puzzle instead of a checkbox. I don't know if i am correct in this thought entirely though! – Shayan Zafar Sep 08 '16 at 19:10

2 Answers2

3

Unfortunately, Google doesn't have an API event to track this, but we can use the Mutation Observer Web API to track DOM changes by Google API on our own. It's supported by browsers pretty well: enter image description here

First, we need to create a target element that we will observe for Google iframe appearance. We are going to target document.body as an iframe will be appended to it:

const targetElement = document.body;

Then we need to create a config object for MutationObserver. Here we can specify what exactly we will tracking in DOM changes. Please note that all values are 'false' by default so we can only leave 'childList' - which means that we will observe only the child node changes for the target element - document.body in our case:

const observerConfig = {
    childList: true,
    attributes: false,
    attributeOldValue: false,
    characterData: false,
    characterDataOldValue: false,
    subtree: false
};

Then we need to create a function that will be invoked when an observer detects a specific type of DOM change that we specified in config object. The first argument represents an array of Mutation Observer objects

function DOMChangeCallbackFunction(mutationRecords) {
    mutationRecords.forEach((mutationRecord) => {
        if (mutationRecord.addedNodes.length) { //check only when notes were added to DOM
            var reCaptchaParentContainer = mutationRecord.addedNodes[0];
            var reCaptchaIframe = reCaptchaParentContainer.querySelectorAll('iframe[title*="recaptcha"]');

            if (reCaptchaIframe.length) { // Google reCaptcha iframe was loaded
                console.log('Yay!');
                reCaptchaObserver.disconnect(); // We don't want to observe more DOM changes for better performance
                // Challenge was loaded -- DO SOMETHING HERE
            }
        }
    });
}

That is almost it. The only things that are left - instantiating an observer itself and starting observing DOM changes:

const reCaptchaObserver = new MutationObserver(DOMChangeCallbackFunction);
reCaptchaObserver.observe(targetElement, observerConfig);

I hope that helps :)

kosmeln
  • 247
  • 2
  • 10
0

Here is how you could do this with setInterval. This could kill the browser if the user never checks the box. You probably want to delay running this until they start filling out the form or wait until they mouseover/focus on the reCaptcha element. You may also want a more specific selector (by wrapping your recaptcha in a div) incase you have other iframes that could use the same title in the code. It's a much better idea to use the mutationObserver as mentioned in the comments. Hope this does what you need.

function isPuzzleDisplayed (selector) {
  /**
   * @param {string} selector
   * @returns boolean - if the specified element exists
   */
    return document.querySelectorAll(selector).length >= 1;
}

var timer = setInterval(function () {
  /**
   * Check if element exists every 0 seconds.
   * If it does, kill timer, else keep running forever!
   * WARNING! This is not performant.
   */
    if (isPuzzleDisplayed('iframe[title*="recaptcha challenge"]')) {
        console.log('But this is how you do it!');
        /* run your code here */
        clearInterval(timer);
        timer = 0;
    } else {
        console.log('It is true the Api however does not return a property or event that would indicate that this change has happened. >', timer);
    }
}, 0);
colecmc
  • 3,133
  • 2
  • 20
  • 33