Essentially I want to have a script execute when the contents of a DIV change. Since the scripts are separate (content script in chrome extension & webpage script), I need a way simply observe changes in DOM state. I could set up polling but that seems sloppy.
相关问题
- Is there a limit to how many levels you can nest i
- How to toggle on Order in ReactJS
- How to fix IE ClearType + jQuery opacity problem i
- void before promise syntax
- jQuery add and remove delay
Many sites use AJAX to add/show/change content dynamically. Sometimes it's used instead of in-site navigation, so current URL is changed programmatically and content scripts aren't automatically executed by browser in this case since the page isn't fetched from remote server entirely.
Usual JS methods of detecting page changes available in a content script.
MutationObserver (docs) to literally detect DOM changes:
Event listener for sites that signal content change by sending a DOM event:
pjax:end
ondocument
used by many pjax-based sites e.g. GitHub,see How to run jQuery before and after a pjax load?
message
onwindow
used by e.g. Google search in Chrome browser,see Chrome extension detect Google search refresh
spfdone
ondocument
used by Youtube,see How to detect page navigation on Youtube and modify HTML before page is rendered?
Periodic checking of DOM via setInterval:
Obviously this will work only in cases when you wait for a specific element identified by its id/selector to appear, and it won't let you universally detect new dynamically added content unless you invent some kind of fingerprinting the existing contents.
Cloaking History API inside an injected DOM script:
Listening to hashchange, popstate events:
Extensions-specific: detect URL changes in a background / event page.
There are advanced API to work with navigation: webNavigation, webRequest, but we'll use simple chrome.tabs.onUpdated event listener that sends a message to the content script:
manifest.json:
declare background/event page
declare content script
add
"tabs"
permission.background.js
content.js
Edit
This answer is now deprecated. See the answer by apsillers.
Since this is for a Chrome extension, you might as well use the standard DOM event -
DOMSubtreeModified
. See the support for this event across browsers. It has been supported in Chrome since 1.0.See a working example here.
Several years later, there is now officially a better solution. DOM4 Mutation Observers are the replacement for deprecated DOM3 mutation events. They are currently implemented in modern browsers as
MutationObserver
(or as the vendor-prefixedWebKitMutationObserver
in old versions of Chrome):This example listens for DOM changes on
document
and its entire subtree, and it will fire on changes to element attributes as well as structural changes. The draft spec has a full list of valid mutation listener properties:(This list is current as of April 2014; you may check the specification for any changes.)
Another approach depending on how you are changing the div. If you are using JQuery to change a div's contents with its html() method, you can extend that method and call a registration function each time you put html into a div.
We just intercept the calls to html(), call a registration function with this, which in the context refers to the target element getting new content, then we pass on the call to the original jquery.html() function. Remember to return the results of the original html() method, because JQuery expects it for method chaining.
For more info on method overriding and extension, check out http://www.bennadel.com/blog/2009-Using-Self-Executing-Function-Arguments-To-Override-Core-jQuery-Methods.htm, which is where I cribbed the closure function. Also check out the plugins tutorial at JQuery's site.
In addition to the "raw" tools provided by
MutationObserver
API, there exist "convenience" libraries to work with DOM mutations.Consider: MutationObserver represents each DOM change in terms of subtrees. So if you're, for instance, waiting for a certain element to be inserted, it may be deep inside the children of
mutations.mutation[i].addedNodes[j]
.Another problem is when your own code, in reaction to mutations, changes DOM - you often want to filter it out.
A good convenience library that solves such problems is
mutation-summary
(disclaimer: I'm not the author, just a satisfied user), which enables you to specify queries of what you're interested in, and get exactly that.Basic usage example from the docs: