【问题标题】:How to stringify event object?如何对事件对象进行字符串化?
【发布时间】:2012-07-17 20:38:55
【问题描述】:

JSON.stringify(eventObject);

给予:

TypeError: Converting circular structure to JSON


dojox.json.ref.toJson(eventObject);

给予:

TypeError: Accessing selectionEnd on an input element that cannot have a selection.


是否有一些库/代码可以用来完成它?

【问题讨论】:

  • 您是否尝试查看结构以从对象中提取某些方法,或者您想要stringify 它的目的是什么?
  • 调试PhoneGap - 将数据结构、事件和堆栈跟踪发送到本机层

标签: javascript json serialization stringify


【解决方案1】:

您将无法使用 JSON.stringify 序列化事件对象,因为事件对象包含对 DOM 节点的引用,并且 DOM 到处都有循环引用(例如,子/父关系)。默认情况下 JSON 无法处理这些,所以你有点不走运。

我建议查看How to serialize DOM node to JSON even if there are circular references?,它对如何序列化 DOM 节点有一些建议。此外,以下问题似乎提供了有用的信息:

能够处理循环引用的 JSON 库似乎是

或者,如果你不需要它们,你可以删除所有对 DOM 节点的引用,然后序列化对象。 毕竟你不应该这样做。见@PointedEars 评论:)

【讨论】:

【解决方案2】:

使用“替换器”功能避免错误:

JSON.stringify(evt, function(k, v) {
    if (v instanceof Node) {
        return 'Node';
    }
    if (v instanceof Window) {
        return 'Window';
    }
    return v;
}, ' ');

2019 年更新:浏览器 API 发生了一些变化,这是一种公开事件原型链中所有可用键的方法

function stringifyEvent(e) {
  const obj = {};
  for (let k in e) {
    obj[k] = e[k];
  }
  return JSON.stringify(obj, (k, v) => {
    if (v instanceof Node) return 'Node';
    if (v instanceof Window) return 'Window';
    return v;
  }, ' ');
}

【讨论】:

  • 这也只导致{isTrusted: true} for MouseEvent's
  • 我之所以对此表示反对,只是因为有限地使用了返回“节点”的函数,而不是您通常希望从事件对象中获得的所有信息。
  • @PrestonBadeer 您可以创建一个自定义算法来处理循环引用、返回构造函数名称、选择器等。这取决于您。
【解决方案3】:

我遇到了类似的问题,并编写了一个简单的事件序列化程序,其中包含一个帮助方法来清理事件的路径属性。此解决方案将数据从事件转换为可序列化对象的方法:

  • 复制原始属性
  • 为事件对象中的元素属性复制outerHTML
  • 计算path属性的选择器路径(这样可以避免复制整个HTML页面的outerHTML)

// Calculate a string representation of a node's DOM path.
var pathToSelector = function(node) {
  if (!node || !node.outerHTML) {
    return null;
  }

  var path;
  while (node.parentElement) {
    var name = node.localName;
    if (!name) break;
    name = name.toLowerCase();
    var parent = node.parentElement;

    var domSiblings = [];

    if (parent.children && parent.children.length > 0) {
      for (var i = 0; i < parent.children.length; i++) {
        var sibling = parent.children[i];
        if (sibling.localName && sibling.localName.toLowerCase) {
          if (sibling.localName.toLowerCase() === name) {
            domSiblings.push(sibling);
          }
        }
      }
    }

    if (domSiblings.length > 1) {
      name += ':eq(' + domSiblings.indexOf(node) + ')';
    }
    path = name + (path ? '>' + path : '');
    node = parent;
  }

  return path;
};

// Generate a JSON version of the event.
var serializeEvent = function(e) {
  if (e) {
    var o = {
      eventName: e.toString(),
      altKey: e.altKey,
      bubbles: e.bubbles,
      button: e.button,
      buttons: e.buttons,
      cancelBubble: e.cancelBubble,
      cancelable: e.cancelable,
      clientX: e.clientX,
      clientY: e.clientY,
      composed: e.composed,
      ctrlKey: e.ctrlKey,
      currentTarget: e.currentTarget ? e.currentTarget.outerHTML : null,
      defaultPrevented: e.defaultPrevented,
      detail: e.detail,
      eventPhase: e.eventPhase,
      fromElement: e.fromElement ? e.fromElement.outerHTML : null,
      isTrusted: e.isTrusted,
      layerX: e.layerX,
      layerY: e.layerY,
      metaKey: e.metaKey,
      movementX: e.movementX,
      movementY: e.movementY,
      offsetX: e.offsetX,
      offsetY: e.offsetY,
      pageX: e.pageX,
      pageY: e.pageY,
      path: pathToSelector(e.path && e.path.length ? e.path[0] : null),
      relatedTarget: e.relatedTarget ? e.relatedTarget.outerHTML : null,
      returnValue: e.returnValue,
      screenX: e.screenX,
      screenY: e.screenY,
      shiftKey: e.shiftKey,
      sourceCapabilities: e.sourceCapabilities ? e.sourceCapabilities.toString() : null,
      target: e.target ? e.target.outerHTML : null,
      timeStamp: e.timeStamp,
      toElement: e.toElement ? e.toElement.outerHTML : null,
      type: e.type,
      view: e.view ? e.view.toString() : null,
      which: e.which,
      x: e.x,
      y: e.y
    };

    console.log(JSON.stringify(o, null, 2));
  }
};

// Create a mock event for this example
var evt = new MouseEvent("click", {
  bubbles: true,
  cancelable: true,
  view: window
});
var cb = document.getElementById("clicker");

// Add a click listener
cb.addEventListener("click", serializeEvent);

// Fire the event
cb.dispatchEvent(evt);
<div>
  <button id="clicker" /> JSONify my click!
</div>

【讨论】:

    【解决方案4】:

    Alexander Shutau 给出的代码的改进版本,因为它处理多级对象 (ES6):

    function stringify_object(object, depth=0, max_depth=2) {
        // change max_depth to see more levels, for a touch event, 2 is good
        if (depth > max_depth)
            return 'Object';
    
        const obj = {};
        for (let key in object) {
            let value = object[key];
            if (value instanceof Node)
                // specify which properties you want to see from the node
                value = {id: value.id};
            else if (value instanceof Window)
                value = 'Window';
            else if (value instanceof Object)
                value = stringify_object(value, depth+1, max_depth);
    
            obj[key] = value;
        }
    
        return depth? obj: JSON.stringify(obj);
    }
    

    只要这样称呼它:

    stringify_object(event, 2);
    

    例如,在一个 touchstart 事件中,我得到这个:

    touchstart : {"isTrusted":true,"touches":{"0":{"identifier":0,"target":{"id":"screen"},"screenX":548,"screenY":281.5,"clientX":498.1817932128906,"clientY":185.90908813476562,"pageX":498.1817932128906,"pageY":185.90908813476562,"radiusX":29.77272605895996,"radiusY":27.954544067382812,"rotationAngle":0,"force":0.5},"length":1,"item":{}},"targetTouches":{"0":{"identifier":0,"target":{"id":"screen"},"screenX":548,"screenY":281.5,"clientX":498.1817932128906,"clientY":185.90908813476562,"pageX":498.1817932128906,"pageY":185.90908813476562,"radiusX":29.77272605895996,"radiusY":27.954544067382812,"rotationAngle":0,"force":0.5},"length":1,"item":{}},"changedTouches":{"0":{"identifier":0,"target":{"id":"screen"},"screenX":548,"screenY":281.5,"clientX":498.1817932128906,"clientY":185.90908813476562,"pageX":498.1817932128906,"pageY":185.90908813476562,"radiusX":29.77272605895996,"radiusY":27.954544067382812,"rotationAngle":0,"force":0.5},"length":1,"item":{}},"altKey":false,"metaKey":false,"ctrlKey":false,"shiftKey":false,"view":"Window","detail":0,"sourceCapabilities":{"firesTouchEvents":true},"which":0,"initUIEvent":{},"NONE":0,"CAPTURING_PHASE":1,"AT_TARGET":2,"BUBBLING_PHASE":3,"type":"touchstart","target":{"id":"screen"},"currentTarget":{"id":"screen"},"eventPhase":2,"bubbles":true,"cancelable":true,"defaultPrevented":false,"composed":true,"timeStamp":192516.7899999651,"srcElement":{"id":"screen"},"returnValue":true,"cancelBubble":false,"path":{"0":{"id":"screen"},"1":{"id":"back"},"2":{"id":""},"3":{"id":""},"4":{},"5":"Window"},"composedPath":{},"stopPropagation":{},"stopImmediatePropagation":{},"preventDefault":{},"initEvent":{}}
    

    【讨论】:

      【解决方案5】:

      不确定它是否有帮助,但我只是在 Angular JS 文档中偶然发现了这一点:

      *来源:https://code.angularjs.org/1.5.5/docs/guide/expression#-event-

      /*
       * return a copy of an object with only non-object keys
       * we need this to avoid circular references
       */
      function simpleKeys (original) {
        return Object.keys(original).reduce(function (obj, key) {
          obj[key] = typeof original[key] === 'object' ? '{ ... }' : original[key];
          return obj;
        }, {});
      }
      

      现在您可以执行以下操作:

      JSON.stringify(simpleKeys(eventObject));
      

      【讨论】:

      • 当在 touchevent 上使用它时,它只返回一个元素 {isTrusted: true}
      • 所有其他MouseEvent's 相同
      【解决方案6】:

      因此,问题在于 JSON.stringify 似乎在找到循环引用后立即退出。反正我对循环引用的属性不感兴趣。我得到其余的方法是

      var str = "{"
      for (var key in data) {
        if (JSON.stringify(data[key]) !== "") {
          str += key + ":" + data[key]) + ",";
        }
      }
      str += "}"
      

      这基本上会给你剩下的属性。为避免 JS 错误,您可以将 if 放入 try/catch。

      【讨论】:

        【解决方案7】:

        只需使用 JSON.stringify(event) 即可,事件数据应转换为字符串。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-11-10
          • 1970-01-01
          • 2015-01-14
          • 1970-01-01
          • 1970-01-01
          • 2016-04-14
          相关资源
          最近更新 更多