Passing clicks through a p5js canvas to DOM elements underneath

Hello,

I have a p5js canvas which is mostly transparent but overlays to the entire screen, so you can still see some of the HTML webpage underneath. The Problem is that the canvas kind of 'intercepts' any clicks because it lays over the top, so you can no-longer click on any links etc on the underlying webpage.

How can I pass through the clicks to the underlying page? I have googled and found that solutions do exist for people working in javascript ( such as this one ) but I don't really know anything about web coding outside of my p5js 'safety net' and the explanations go over my head a bit. Is there a native p5js method for this or can anyone explain how I step outside the p5 DOM environment to hook this up?

Edit: Forgot to mention it would be ideal to be able control if/when the click is passed through, ie. depending on mouse position or if a variable returns true/false etc.

Thank you

Marcus

Answers

  • Got no idea how to pull that out. Still very novice about DOM stuff. X_X
    Yet I've got some tips for ya...

    In p5.js all DOM created by its API is a p5.Element object:
    http://p5js.org/reference/#/p5.Element

    It's nothing more than a mere wrapper over some HTMLElement w/ extra properties & methods.

    However we can access the actual underlying HTMLElement via its property elt:
    http://p5js.org/reference/#/p5.Element/elt

    For the canvas element that'd be something like this:

    let cnv, cnvElt;
    
    function setup() {
      cnv = createCanvas(800, 600).mousePressed(canvasMousePressed);
      cnvElt = cnv.elt;
    }
    
    function canvasMousePressed(evt) {
      // Decide whether to forward click or not...
    }
    

    Maybe you're gonna need to use select() in order to get the element behind the canvas:
    http://p5js.org/reference/#/p5/select

  • Thanks GoToLoop, that looks like a helpful start.

    I have two questions though about code/concepts I don't recognise. What is the meaning of the 'let' in Line 1? How is it different to 'var'?

    Secondly adding a .mousePressed method to the createCanvas, I've never seen that before. Does this mean it will run that custom function when mouseIsPressed on the canvas (and not outside)? And the 'evt' parameter, what is that for? Why do it this way as opposed to just using p5's default mousePressed() function?

    Thank you very much!

    Marcus

  • edited July 2016
    1. https://GitHub.com/metagrover/ES6-for-humans#1-let-const-and-block-scoping

    2. Lotsa questions. :P Here it comes:

    Does this mean it will run that custom function when mousePressed() on the canvas (and not outside)?

    Exactly! Canvas is of datatype p5.Renderer: http://p5js.org/reference/#/p5.Renderer
    Which also inherits from p5.Element like most of the API: http://p5js.org/reference/#/p5.Element

    And the evt parameter, what is that for?

    It's not needed in most cases. We can simply erase it if we wanna. ;))
    More specifically, that parameter is of datatype MouseEvent:
    https://developer.Mozilla.org/en-US/docs/Web/API/MouseEvent

    ... as opposed to just using p5's default mousePressed() function?

    p5::mousePressed() catches clicks from anywhere within the webpage:
    http://p5js.org/reference/#/p5/mousePressed

  • @marcusround: amusingly (or perhaps not :/ ) I spent an hour this morning exploring workarounds only to find a trivial solution at the bottom of the post you linked to:

    UPDATE (12-1-09): Seems that Mozilla has also noticed this issue.

    I wish people read things properly before posting here instead of expecting us to read documentation for them :P

    The answer is to apply the CSS pointer-events: none; to the sketch canvas...

    This demo also includes one (limited and onerous) approach I was exploring as a workaround:

    index.html

    <html>
    
    <head>
        <title>instance mode demo</title>
    
        <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.5/p5.js"></script>
    
        <style>
            body, #wrapper {
                position: relative; 
            }
    
            #wrapper {
                position: absolute;
                top: 10;
                left: 10;
            }
    
            #button1 {
                position: absolute;
                top: 30;
                left: 100;
                background: #333;
                color: #fff;
                width: 140px;
                height: 30px;
                padding: 10px;
                z-index: 10;
                text-align: center;
            }
    
            #button1:hover {
                background: #666;
                cursor: pointer;
            }
    
            /* without this the absolutely positioned button
               appears above sketch
            */
            #sketch01 canvas {
                z-index: 999;
                position: absolute;
                top: 0;
                left: 0;
                pointer-events: none;
            }
        </style>
    
    </head>
    
    <body>
    
        <div id="wrapper">
          <div id="button1">click me</div>
          <p>this is a <a href="https://p5js.org/">link</a>
          </p> 
          <div id="sketch01"></div>
        </div>
    
        <script src="js/sketch.js"></script>
    </body>
    
    </html>
    

    sketch.js

    var btn1 = {};
    
    // p5 launched in instance mode
    new p5(function(p){
    
        p.setup = function() {
          p.createCanvas(400, 300);
          initButton();
        }
    
        p.draw = function() {
            // clear is required with semi-transparent background
            p.clear();
            p.background("rgba(255,166,0,0.5)");
        }
    
        p.mousePressed = function(evt) {
          console.log("canvas clicked");
          // manual check to see if button is clicked
          btn1.isMouseClicked(p.mouseX, p.mouseY);
        }
    
    }, "sketch01");
    
    
    function initButton() {
    
        // get button location and dimensions
        // NOTE: see possible issues with this below
        btn1Elt = document.getElementById("button1");
        btn1.x = btn1Elt.offsetLeft;
        btn1.y = btn1Elt.offsetTop;
        btn1.w = btn1Elt.offsetWidth;
        btn1.h = btn1Elt.offsetHeight;
    
        // test if button is clicked based on location of click on canvas
        // NOTE: this is reliant on button being absolutely positioned
        // in the same container as the sketch.
        btn1.isMouseClicked = function(x,y) {
            if (x>this.x 
                && x<this.x+this.w
                && y > this.y
                && y < this.y + this.h) {
                console.log("button clicked via workaround");
            }
        }
    
        // a manually added event listener on the button
        // element itself.
        btn1Elt.addEventListener("click", function() {
            console.log("button element clicked");
        }, false);
    
    }
    

    caniuse shows good levels of support for pointer events. At first glance the docs suggest that using this should stop events being triggered on the canvas; but the notes suggest why it works nicely for us in this context.

    Note: I may not have used the best method for determining the size of the button - see MDN

  • I wish people read things properly before posting here instead of expecting us to read documentation for them :P

    I don't think this is a fair or helpful comment. I explicitly said in my first post that I had found existing solutions but that they were over my head and I wanted help deciphering and applying them.

    Nevertheless thank you for your reply and I will try to use your contribution to understand and solve my problem.

  • I don't think this is a fair or helpful comment.

    Sorry - was meant to some extent with tongue in cheek; hence the :P

    And I wouldn't have taken that tone if I hadn't then made the effort to demonstrate the solution: you'll find that the remainder of my answer is helpful...

    The one thing I will say is that it's worth looking out for updates on posts like the one you linked to. Such updates often contain a simpler solution (or at least more relevant info) than the one originally offered. In this case the solution is as simple as applying a bit of CSS; so involves absolutely no scripting and wasn't at all difficult to interpret:

    When pointer-events is set to none, pointer events are passed through the target element and are instead sent to the element that is underneath it.

    If you didn't understand how to apply this then I'd suggest spending some time studying the basics:

    There are sometimes DOM/CSS solutions that can save on a huge amount of effort/coding; so it's worth having a basic understanding before delving too far into p5/JavaScript ;)

  • Thank you. Processing/p5 has been my entry point to the world of programming and I am enjoying JavaScript, but this is my first foray into CSS. It seems like a much different 'style' of coding, more about knowing the pre-existing 'magic words' that are hard-coded into CSS rather than creating your own system of interlocking functions in JS.

Sign In or Register to comment.