document.elementFromPoint(x, y) isn't reportin

2019-08-26 03:14发布

问题:

According to this answer, I should be able to get the element that an element "drop" occurred over. However, it's reporting really odd and random results which always seems to be parent elements instead of the actual child element the dragged element is dropped on.

Here's the FULL CODE EXAMPLE on CodeSandbox with the pertinent snippets shown below...

<div>
    <div id="sidebar">
      <drag :transfer-data="{ type: 'textbox', width: 330, height: 50 }" class="draggable-item"
        >Textbox
        <div slot="image" class="drag-image"><div class="field-container" style="width: 330px;height: 50px;">Texbox</div></div></drag
      >
    </div>
    <div id="pages-container" @click.self="deselectAll();">
      <b-alert show variant="info" class="text-center">Drag controls from the left sidebar to the pages below.</b-alert>
      <drop
        class="page-container"
        v-for="(p, index) in pages"
        @drop="onDrop"
        @click.self="deselectAll();"
        :data-page-number="p.pageNumber"
        :key="p.pageNumber + '_page_' + index"
        :style="{ width: p.width + 'px', height: p.height + 'px' }"
      >
        <img src="https://www.kirupa.com/flash/images/single_column_text.png" width="100%" height="100%" :data-page-number="p.pageNumber" @click.self="deselectAll();" />
        <div style="position: absolute;top:0;bottom:0;left:0;right:0;background-color:white;opacity:0" :data-page-number="p.pageNumber" @click.self="deselectAll();"></div>

        <vue-draggable-resizable
          :id="f.id"
          :style="{ 'z-index': '1044 !important' }"
          :class="{ 'field-container grabbable': true, selected: f.isSelected }"
          v-for="(f, f_index) in getPageFields(fields, p.pageNumber)"
          :key="f.id"
          :min-width="20"
          :min-height="20"
          :x="f.left"
          :y="f.top"
          :w="f.width"
          :h="f.height"
          :parent="false"
          :z-index="f.isSelected ? '1045 !important' : '1044 !important'"
          :prevent-deactivation="true"
          :active.sync="f.isSelected"
          @resizestop="onResizeStop"
          @dragstop="onDragStop"
          @activated="onActivated(f.id);"
          @clicked="onActivated(f.id);"
          >id: {{ f.id }} <br />
          page: {{ p.pageNumber }} fieldIndex: {{ f_index }} selected: {{ f.isSelected }}</vue-draggable-resizable
        >
      </drop>
    </div>
</div>

The pertinent JavaScript is shown here (the page number alert test is always undefined)...

onDragStop(x, y) {
  //...
  const el = document.elementFromPoint(x, y);
  alert("Dropped onto page number: " + el.dataset.pageNumber);
}

回答1:

As @Joao pointed out, document.elementFromPoint() requires viewport coordinates, but VueDraggableResizable provides offset coordinates (based on its configured lock aspect ratio and bounds) relative to the initial drop-element. You could still get the viewport coordinates by grabbing them from the draggable item itself.

Steps:

  1. Add a ref to <vue-draggable-resizable> (which will create an array of references to the draggable), and update the dragstop-handler to also take an index that we'll use with the applicable ref in the next step:

    // template
    <vue-draggable-resizable
        v-for="(f, f_index) in ..."
        ref="draggable"