npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

svg-drag-select

v0.4.2

Published

A vanilla-js module for adding select-on-drag behavior to inline SVG elements.

Downloads

311

Readme

svg-drag-select

Add select-on-drag behavior to inline SVG elements.
No dependencies and lightweight (~ 1.8 kB minified gzipped).
Demo

Installation

via npm (with a module bundler)

$ npm i svg-drag-select
import svgDragSelect from "svg-drag-select"

via CDN (jsDelivr)

<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script>/* `window.svgDragSelect` function is available */</script>

Usage and options

  • JavaScript
const {
  cancel,           // cleanup funciton.
                    // please call `cancel()` when the select-on-drag behavior is no longer needed.
  dragAreaOverlay,  // a div element overlaying dragging area.
                    // you can customize the style of this element.
                    // this element has "svg-drag-select-area-overlay" class by default.
} = svgDragSelect({
  // the svg element (required).
  svg: document.getElementById("my-svg"),

  // followings are optional parameters with default values.
  referenceElement: null,     // selects only descendants of this SVGElement if specified.
  selector: "enclosure",      // "enclosure": selects enclosed elements using getEnclosureList().
                              // "intersection": selects intersected elements using getIntersectionList().
                              // function: custom selector implementation

  // followings are optional selection handlers
  onSelectionStart({
    svg,                      // the svg element.
    pointerEvent,             // a `PointerEvent` instance with "pointerdown" type.
                              // (in case of Safari, a `MouseEvent` or a `TouchEvent` is used instead.)
    cancel,                   // cancel() cancels.
  }) {
    // for example: handles mouse left button only.
    if (pointerEvent.button !== 0) {
      cancel()
      return
    }
    // for example: clear "data-selected" attribute
    const selectedElements = svg.querySelectorAll('[data-selected]')
    for (let i = 0; i < selectedElements.length; i++) {
      selectedElements[i].removeAttribute('data-selected')
    }
  },

  onSelectionChange({
    svg,                      // the svg element.
    pointerEvent,             // a `PointerEvent` instance with either a "pointerdown" event or a "pointermove" event.
                              // (in case of Safari, a `MouseEvent` or a `TouchEvent` is used instead.)
    selectedElements,         // selected element array.
    previousSelectedElements, // previous selected element array.
    newlySelectedElements,    // `selectedElements - previousSelectedElements`
    newlyDeselectedElements,  // `previousSelectedElements - selectedElements`
  }) {
    // for example: toggle "data-selected" attribute
    newlyDeselectedElements.forEach(element => element.removeAttribute('data-selected'))
    newlySelectedElements.forEach(element => element.setAttribute('data-selected', ''))
  },

  onSelectionEnd({
    svg,                      // the svg element.
    pointerEvent,             // a `PointerEvent` instance with either a "pointerup" event or a "pointercancel" event.
                              // (in case of Safari, a `MouseEvent` or a `TouchEvent` is used instead.)
    selectedElements,         // selected element array.
  }) {
  },
})

// cleanup for when the select-on-drag behavior is no longer needed
// (including unbinding of the event listeners)
cancel()
  • CSS
/* please setup drag area overlay styles. for example: */
.svg-drag-select-area-overlay {
  border: 1px dotted gray;
  background-color: rgba(255,255,255,.4);
}

Custom Selector

You may need to implement your own selector function because:

  • If "intersection" is specified as selector option,
  • Implementing a good "intersection" selector is so hard for me because stricity and performance are in a trade-off relationship.
    • (BTW, IE 11 seems to have a good SVGSVGElement.prototype.getIntersectionList() implementation...)

The following is a custom selector example written for demo.

const strictIntersectionSelector = ({
  svg,                            // the svg element.
  referenceElement,               // please select only descendants of this SVGElement if specified.
  pointerEvent,                   // a `PointerEvent` instance with either a "pointerdown" event or a "pointermove" event.
                                  // (in case of Safari, a `MouseEvent` or a `TouchEvent` is used instead.)
  dragAreaInClientCoordinate,     // a `SVGRect` that represents the dragging area in client coordinate.
  dragAreaInSvgCoordinate,        // a `SVGRect` that represents the dragging area in svg coordinate.
  dragAreaInInitialSvgCoordinate, // a `SVGRect` that represents the dragging area in initial viewport coordinate of the svg.
  getEnclosures,                  // `getEnclosures()` returns elements enclosed in the dragging area.
  getIntersections,               // `getIntersections()` returns elements intersect the dragging area.
                                  // Chrome, Safari and Firefox checks only bounding box intersection.
}) => getIntersections().filter(element => {
  // the element that the pointer event raised is considered to intersect.
  if (pointerEvent.target === element) {
    return true
  }
  // strictly check only <path>s.
  if (!(element instanceof SVGPathElement)) {
    return true
  }
  // check if there is at least one enclosed point in the path.
  for (let i = 0, len = element.getTotalLength(); i <= len; i += 4 /* arbitrary */) {
    const { x, y } = element.getPointAtLength(i)
    if (
        dragAreaInSvgCoordinate.x <= x && x <= dragAreaInSvgCoordinate.x + dragAreaInSvgCoordinate.width &&
        dragAreaInSvgCoordinate.y <= y && y <= dragAreaInSvgCoordinate.y + dragAreaInSvgCoordinate.height
    ) {
      return true
    }
  }
  return false
})

svgDragSelect({
  selector: strictIntersectionSelector,
  /* ... */
})

License

WTFPL