skip to Main Content

I want to replay 1:1 the all click events. Unfortunately, it doesn’t work for a dropdown. Whenever I select a value in a dropdown, it shows the wrong position (feel free to test it in my code snipped). I took the event handler from https://stackoverflow.com/a/61732450 and it worked like a charm for all my work, except here.

Left: What I see
Right: What I want

enter image description here

Thank you very much

let touch_positions = [];
function process_touchevent(e) {
  if (
    e.type == "touchstart" ||
    e.type == "touchmove" ||
    e.type == "touchend" ||
    e.type == "touchcancel"
  ) {
    var evt = typeof e.originalEvent === "undefined" ? e : e.originalEvent;
    var touch = evt.touches[0] || evt.changedTouches[0];
    x = touch.pageX;
    y = touch.pageY;
  } else if (
    e.type == "click" ||
    e.type == "mousedown" ||
    e.type == "mouseup" ||
    e.type == "mousemove" ||
    e.type == "mouseover" ||
    e.type == "mouseout" ||
    e.type == "mouseenter" ||
    e.type == "mouseleave"
  ) {
    x = e.clientX;
    y = e.clientY;
  }
  touch_positions.push({ x, y, type: e.type, date: new Date() });

  var elemDiv = document.createElement("div");
  elemDiv.style.cssText =
    "position:absolute;width:10px;height:10px;border-radius: 10px;opacity:0.3;z-index:3;background:red;";
  let position_text = "left:" + x + "px;top:" + y + "px;";
  elemDiv.style.cssText += position_text;
  document.body.appendChild(elemDiv);
}

window.addEventListener("touchstart", process_touchevent, false);
window.addEventListener("touchmove", process_touchevent, false);
window.addEventListener("touchcancel", process_touchevent, false);
window.addEventListener("touchend", process_touchevent, false);
window.addEventListener("click", process_touchevent, false);

function draw_all_touch_positions() {
  touch_positions.forEach(function (li) {
    var elemDiv = document.createElement("div");
    elemDiv.style.cssText =
      "position:absolute;width:10px;height:10px;border-radius: 10px;opacity:0.3;z-index:3;background:red;";
    let position_text = "left:" + li.x + "px;top:" + li.y + "px;";
    elemDiv.style.cssText += position_text;
    document.body.appendChild(elemDiv);
  });
  touch_positions = [];
}
<select name="cars" id="cars">
  <option value="volvo">Volvo</option>
  <option value="saab" selected="">Saab</option>
  <option value="mercedes">Mercedes</option>
  <option value="audi">Audi</option>
</select>

<button onclick='draw_all_touch_positions()'>Draw</button>

2

Answers


  1. Using mousedown instead of click seems to work for the initial dropdown clicks, but not the <option>‘s

    let touch_positions = [];
    function process_touchevent(e) {
      if (
        e.type == "touchstart" ||
        e.type == "touchmove" ||
        e.type == "touchend" ||
        e.type == "touchcancel"
      ) {
        var evt = typeof e.originalEvent === "undefined" ? e : e.originalEvent;
        var touch = evt.touches[0] || evt.changedTouches[0];
        x = touch.pageX;
        y = touch.pageY;
      } else if (
        e.type == "click" ||
        e.type == "mousedown" ||
        e.type == "mouseup" ||
        e.type == "mousemove" ||
        e.type == "mouseover" ||
        e.type == "mouseout" ||
        e.type == "mouseenter" ||
        e.type == "mouseleave"
      ) {
        x = e.clientX;
        y = e.clientY;
      }
      touch_positions.push({ x, y, type: e.type, date: new Date() });
    
      var elemDiv = document.createElement("div");
      elemDiv.style.cssText =
        "position:absolute;width:10px;height:10px;border-radius: 10px;opacity:0.3;z-index:3;background:red;";
      let position_text = "left:" + x + "px;top:" + y + "px;";
      elemDiv.style.cssText += position_text;
      document.body.appendChild(elemDiv);
    }
    
    window.addEventListener("touchstart", process_touchevent, false);
    window.addEventListener("touchmove", process_touchevent, false);
    window.addEventListener("touchcancel", process_touchevent, false);
    window.addEventListener("touchend", process_touchevent, false);
    window.addEventListener("mousedown", process_touchevent, false);
    
    function draw_all_touch_positions() {
      touch_positions.forEach(function (li) {
        var elemDiv = document.createElement("div");
        elemDiv.style.cssText =
          "position:absolute;width:10px;height:10px;border-radius: 10px;opacity:0.3;z-index:3;background:red;";
        let position_text = "left:" + li.x + "px;top:" + li.y + "px;";
        elemDiv.style.cssText += position_text;
        document.body.appendChild(elemDiv);
      });
      touch_positions = [];
    }
    <select name="cars" id="cars">
      <option value="volvo">Volvo</option>
      <option value="saab" selected="">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </select>
    
    <button onclick='draw_all_touch_positions()'>Draw</button>
    Login or Signup to reply.
  2. I have two potential solution here.

    A simple and obious solution to me seemed to be just track the mouse movement at all times, and just record it on a click event. Simple and even less code than that you had before!

    However, <select>‘s apparently are very strange in that they do not record any mouse events at all! You’ll notice that the mouse move events stop being logged out when the menu is open and the mouse moves over them! The only exception is when you specify size="2" or larger.

    Very lame!

    const mousePos = {x:0, y:0};
    let touch_positions = [];
    function process_touchevent(e) {
      console.log(e.type, mousePos.x, mousePos.y)
      touch_positions.push({ x: mousePos.x, y: mousePos.y, type: e.type, date: new Date() });
      draw_touch_position(mousePos.x, mousePos.y);
    }
    
    window.addEventListener("touchstart", process_touchevent, false);
    window.addEventListener("touchmove", process_touchevent, false);
    window.addEventListener("touchcancel", process_touchevent, false);
    window.addEventListener("touchend", process_touchevent, false);
    window.addEventListener("mousedown", process_touchevent, false);
    
    window.addEventListener("mousemove", (ev)=>{
      mousePos.x = ev.clientX;
      mousePos.y = ev.clientY;
      console.log('move', mousePos.x, mousePos.y)
    }, false);
    
    function draw_all_touch_positions() {
      touch_positions.forEach(li => {
        draw_touch_position(li.x, li.y);
      });
      touch_positions = [];
    }
    
    function draw_touch_position(x, y){
      var elemDiv = document.createElement("div");
      elemDiv.classList.add("touch-point");
      elemDiv.style.cssText = `left:${x}px;top:${y}px;`;
      document.body.appendChild(elemDiv);
    }
    .touch-point {
      position:absolute;
      width:10px;
      height:10px;
      border-radius: 10px;
      opacity:0.3;
      z-index:3;
      background:red;
      transform: translate(-50%, -50%); /*Make the point appear in the center of the x/y coordinates*/
      pointer-events:none; /*don't allow these to become click targets, clicks will pass through now*/
    }
    <div id="touch-capture"></div>
    
    <select name="cars" id="cars" size="1">
      <option value="volvo">Volvo</option>
      <option value="saab" selected="">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </select>
    
    <select name="cars" id="cars" size="2">
      <option value="volvo">Volvo</option>
      <option value="saab" selected="">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </select>
    
    <input type="text">
    <input type="checkbox">
    
    <button type="button" onclick='draw_all_touch_positions()'>Draw</button>

    In this next one, since it seems clicks on <option>‘s cannot be captured, we can at least try an estimate the option position by getting the selected option index and multiplying that by what we expect the height of the option to be. Option sizes cannot be styled by CSS and might vary between different browsers and OS’s so this might work if you can know these sizes.

    let touch_positions = [];
    function process_touchevent(e) {
      if (/^touch(start|move|end|cancel)$/i.test(e.type)) {
        var evt = typeof e.originalEvent === "undefined" ? e : e.originalEvent;
        var touch = evt.touches[0] || evt.changedTouches[0];
        x = touch.pageX;
        y = touch.pageY;
      } else if (/^click|mouse(down|up|move|over|out|enter|leave)$/i.test(e.type)) {
        x = e.clientX;
        y = e.clientY;
      }
      //consolelog(document.elementFromPoint(x,y));
      
      //Cannot get positions for `<option>` elements, so we must estimate!
      if(e.target.tagName === "OPTION"){
        //console.log(e.target.getBoundingClientRect())
        //console.log(getComputedStyles(e.target))
        const selectedIndex = e.target.parentElement.selectedIndex;
        const optLineHeight = 25; //this might vary in different browsers and OS's!!!!
        const parentRect = e.target.parentElement.getBoundingClientRect();
        const optMenucenterX = parentRect.x + (parentRect.width / 2);
        const optMenuStartY = parentRect.y + parentRect.height;
        
        x = optMenucenterX;
        y = optMenuStartY + (optLineHeight * selectedIndex) + 10;
      }
      
      touch_positions.push({ x, y, type: e.type, date: new Date() });
      draw_touch_position(x, y);
    }
    
    window.addEventListener("touchstart", process_touchevent, false);
    window.addEventListener("touchmove", process_touchevent, false);
    window.addEventListener("touchcancel", process_touchevent, false);
    window.addEventListener("touchend", process_touchevent, false);
    window.addEventListener("mousedown", process_touchevent, false);
    
    function draw_all_touch_positions() {
      touch_positions.forEach(li => {
        draw_touch_position(li.x, li.y);
      });
      touch_positions = [];
    }
    
    function draw_touch_position(x, y){
      var elemDiv = document.createElement("div");
      elemDiv.classList.add("touch-point");
      elemDiv.style.cssText = `left:${x}px;top:${y}px;`;
      document.body.appendChild(elemDiv);
    }
    .touch-point {
      position:absolute;
      width:10px;
      height:10px;
      border-radius: 10px;
      opacity:0.3;
      z-index:3;
      background:red;
      transform: translate(-50%, -50%); /*Make the point appear in the center of the x/y coordinates*/
      pointer-events:none; /*don't allow these to become click targets, clicks will pass through now*/
    }
    <div id="touch-capture"></div>
    
    <select name="cars" id="cars">
      <option value="volvo">Volvo</option>
      <option value="saab" selected="">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </select>
    
    <input type="text">
    <input type="checkbox">
    
    <button type="button" onclick='draw_all_touch_positions()'>Draw</button>

    It’s for sure not perfect but it’s a whole lot closer to your goal. Hopefully this is the only exception that has to be made!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search