注入 js 编程应该知道的一些基础知识,以防混淆造成的错误
要在不干扰原有网页事件的前提下,为元素添加自定义事件处理程序,你可以使用 MutationObserver API 或者通过事件委托的方式来实现。
使用 MutationObserver
MutationObserver
是一个可以监听 DOM 树变化的接口,适用于监听元素属性、子节点等的变化。如果你想监听某个元素的变化(如属性、子节点等),可以使用这个 API。
以下是一个简单的例子,展示如何使用MutationObserver
来监听元素的变化,并在变化时执行自定义逻辑:
使用事件委托
如果你想监听的是某些特定事件(如点击、输入等),可以使用事件委托。事件委托是基于事件冒泡的原理,你可以在父元素上监听事件,然后根据事件的目标元素来判断是否是你关心的元素,从而执行相应的逻辑。
示例代码:
这两种方法各有优缺点,MutationObserver
适用于监听 DOM 变化,而事件委托适用于处理特定类型的事件。你可以根据实际需求选择合适的方法。
使用事件委托的方式添加的逻辑不会直接干扰或阻止原始的事件触发逻辑,除非你明确地使用了event.stopPropagation()
或event.preventDefault()
。
在事件委托的情况下,你是在更高一级的节点(通常是一个父元素)监听事件,然后根据事件的目标(event.target
)来判断是否执行某些逻辑。这种方法本质上是添加了一个新的事件监听器,不会影响到目标元素上已经存在的事件监听器。
例如,如果一个按钮已经有一个点击事件监听器,当你通过事件委托在其父元素上也监听点击事件时,点击该按钮将会触发两个事件处理程序:原始按钮上的处理程序和父元素上的处理程序。除非你在父元素的处理程序中调用了event.stopPropagation()
(这会阻止事件继续冒泡)或event.preventDefault()
(这会阻止事件的默认行为,但不会阻止事件传播),否则原始的事件处理逻辑不会受到影响。
这样,无论原始的按钮上有什么事件监听器,都会按预期工作,同时,你通过事件委托添加的逻辑也会执行,而不会相互干扰。
直接在元素上注册事件监听器(例如使用element.addEventListener
方法)不会覆盖或删除该元素上已经存在的事件监听器,无论这些监听器是通过 HTML 属性(如onclick
)、通过 JavaScript 使用相同的addEventListener
方法,还是以其他方式添加的。
JavaScript 的 addEventListener
方法允许你在同一个元素上为同一事件类型注册多个事件处理函数。这些处理函数会根据它们被添加的顺序依次被调用,而不会相互覆盖。
例如,考虑以下情况:
在这个例子中,当用户点击 myButton
按钮时,两个事件监听器都会被触发。首先触发“Original click event listener”,然后触发“Injected click event listener”,而原始的事件监听器并不会被覆盖或删除。
如果你不希望继续触发特定的事件监听器,可以使用removeEventListener
方法移除它,但你需要有对原始监听器函数的引用,这在注入脚本时可能不可行,除非原始的事件处理函数是以全局可访问的方式定义的。
总的来说,直接在元素上使用addEventListener
添加事件监听器是安全的,不会干扰网页上已经存在的事件处理逻辑。
实际上,event.stopPropagation()
的作用是阻止事件继续在 DOM 树中传播,这与父元素事件和子元素事件的触发顺序有关,但需要明确的是:事件的传播顺序遵循特定的规则,即事件捕获阶段(从外向内)、目标阶段、和事件冒泡阶段(从内向外)。event.stopPropagation()
可以在这三个阶段的任何一个阶段被调用来阻止事件的进一步传播。
事件传播的三个阶段:
捕获阶段:事件从
document
对象传导到事件目标的父节点上,一直向下传导至目标元素,如果在此阶段的监听器中调用了event.stopPropagation()
,则事件不会继续传导至目标元素或不会进入冒泡阶段。目标阶段:事件到达目标元素,此时是事件监听器被触发的时刻。如果事件监听器在目标上调用了
event.stopPropagation()
,事件将不会进入冒泡阶段。冒泡阶段:事件从事件目标向上冒泡到
document
对象。如果在冒泡阶段的某个监听器中调用了event.stopPropagation()
,事件将不会继续向上冒泡至document
对象。
关于父元素和子元素事件触发的顺序:
如果两个事件监听器(一个在父元素上,一个在子元素上)都在冒泡阶段注册,那么子元素的事件监听器会先触发,然后是父元素的事件监听器。因为事件是从目标元素开始冒泡向上的。
如果两个事件监听器都在捕获阶段注���,那么父元素的事件监听器会先触发,然后是子元素的事件监听器。因为事件是从
document
向下传导至目标元素的。如果在子元素的事件监听器中调用了
event.stopPropagation()
,那么无论是在捕获阶段还是冒泡阶段,父元素的事件监听器都不会被触发,因为事件的传播被中止了。
因此,event.stopPropagation()
确实可以影响事件的传播,包括阻止父元素的事件监听器被触发,但具体影响取决于事件监听器是如何注册的(捕获阶段还是冒泡阶段),以及event.stopPropagation()
是在哪个阶段被调用的。
实际上,您的理解有一点误区。让我们澄清一下:
当事件开始传播时,它首先经过捕获阶段,从document
对象向下传递到目标元素的路径上的每个节点。如果在这个过程中的某个父元素上的事件监听器调用了event.stopPropagation()
,这确实会阻止事件继续向下传递到目标元素或更深层的子元素。但是,这种情况比较少见,因为大多数时候,事件监听器是在冒泡阶段而不是捕获阶段注册的。
在目标阶段,事件到达了目标元素。此时,如果目标元素上的事件监听器调用了event.stopPropagation()
,那么事件将不会进入冒泡阶段,即不会从目标元素向上传递回document
。
如果父元素中的事件监听器在捕获阶段调用了event.stopPropagation()
,这确实会阻止事件继续向下传递到更深层的子元素,但这只适用于那些在捕获阶段注册的监听器。对于大多数用户交互事件(如点击),监听器通常是在冒泡阶段注册的。
让我们来纠正一下概念:
如果父元素在捕获阶段的监听器中调用了
event.stopPropagation()
,那么它实际上是阻止了事件继续向内部的子元素传播。这时候,事件还没有到达目标元素,所以目标元素及其内部的子元素的监听器都不会被触发。如果父元素或目标元素在目标阶段或冒泡阶段的监听器中调用了
event.stopPropagation()
,那么它会阻止事件继续向上冒泡到更高层的父元素。
因此,如果您的目的是不让事件从子元素冒泡到父元素,您应该在子元素的事件监听器中调用event.stopPropagation()
。如果在父元素的捕获阶段监听器中调用event.stopPropagation()
,则实际上是阻止了事件向该父元素内部传播,这与阻止冒泡是不同的行为。
您的理解是正确的。当事件在冒泡阶段向上传递时,如果父元素的事件处理程序中调用了event.preventDefault()
,这并不会阻止子元素的事件处理程序的执行。实际上,event.preventDefault()
和event.stopPropagation()
作用是不同的:
event.preventDefault()
用于阻止事件的默认行为,但不会阻止事件在 DOM 树中的传播。例如,如果在链接(<a>
)的点击事件监听器中调用event.preventDefault()
,它会阻止链接的默认跳转行为,但事件仍然会冒泡。event.stopPropagation()
用于阻止事件在 DOM 树中进一步的传播,无论是冒泡还是捕获阶段,但不会影响事件的默认行为。
因此,如果一个子元素的事件首先被触发(例���,用户点击了一个按钮),并且该事件在冒泡阶段向上传递到了父元素,那么在到达父元素之前,子元素上的事件处理程序已经执行了。如果父元素的事件处理程序中调用了event.preventDefault()
,这只会影响到父元素想要阻止的默认行为(如果有的话),而不会影响到子元素的事件处理程序的执行。
简而言之,event.preventDefault()
在父元素的事件处理程序中被调用时,不会阻止子元素的事件处理程序的执行,因为那时子元素的事件处理程序已经执行过了。event.preventDefault()
的作用是阻止事件的默认行为,而不是阻止事件的传播。
在您提供的代码示例中,通过event.preventDefault()
调用阻止了默认行为,这个调用实际上是针对当前触发的事件本身,而不是针对特定的事件监听器。换句话说,event.preventDefault()
的作用是阻止与当前触发的事件相关联的默认行为,而不是阻止之前或之后注册的同类事件监听器中的代码执行。
在您的示例中,无论是原始脚本中注册的点击事件监听器,还是通过 Chrome Extension 注入的脚本添加的监听器,都会正常执行其内部的逻辑(即打印信息到控制台)。当事件到达第三个监听器,执行event.preventDefault()
时,它会阻止与点击事件相关联的默认行为(如果有的话,比如点击链接导致的页面跳转),但不会影响其他已经添加到该元素上的点击事件监听器的执行。
这意味着,所有为myButton
元素注册的点击事件监听器都会按照它们被添加的顺序执行,而event.preventDefault()
只会阻止点击事件的默认行为(如果点击事件自身有默认行为)。它不会阻止其他监听器中的代码执行,也不会影响事件在 DOM 树中的传播,除非显式调用event.stopPropagation()
。
因此,对于您的问题,event.preventDefault()
不会阻止该元素上之前注册过的同类事件的执行。所有的事件监听器都会按顺序执行,event.preventDefault()
只是阻止了事件的默认行为。
很高兴能帮到您!事件处理、事件传播以及默认行为的阻止确实是 Web 开发中的重要概念,它们之间的区别和联系有时会让人混淆,特别是在复杂的事件处理逻辑中。如果您有任何其他问题,无论是关于这个主题或者其他编程、技术相关的问题,都欢迎随时提问。我在这里帮助您澄清疑惑、解决问题。
更多精彩内容👉:开发者网站---网友热议
评论