I enjoy running custom scripts on pages that I do not own or control. Many times these pages have dynamically created content that I would like to apply a function to.
Is this possible? If so, how can I do this? Ideally I am looking for something live jQuery's live method, except instead of binding an event like click it would be more like an event that happens when the element is loaded in the DOM. load event would work for some elements but I don't think for all...
For this question, assume that you cannot look at or change the code that is inserting the DOM nodes. I would like a technique that I could use in a userscript or bookmarklet that could be used across multiple unrelated sites.
Edit: I am looking for something to use on my invert colors bookmarklet: JavaScript: Invert color on all elements of a page
Assuming you're running a browser like Firefox or Chrome, you could listen for the DOMNodeInserted event:
$(document).on('DOMNodeInserted', function(e) {
$(e.target).css({ color : '#c00' });
});
$('body').append('<div>test</div>');
Fiddle: http://jsfiddle.net/VeF6g/ (probably fails in IE)
Update:
The event is deprecated. You should use a MutationObserver:
var observer = new MutationObserver(function(mutationList) {
for (var mutation of mutationList) {
for (var child of mutation.addedNodes) {
child.style.color = '#c00';
}
}
});
observer.observe(document, {childList: true, subtree: true});
// ready? Then stop listening with
observer.disconnect();
More information here: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
If you don't have access to a library like jQuery, here is the syntax in only-JavaScript :
document.addEventListener('DOMNodeInserted', nodeInsert)
function nodeInsert () {
event.srcElement.style.color = '#ffffff'
}
I think it should work in most browsers.
Here is one nice way to handle the node insertion event. set animations for the element that you are going to add using css.
<style type="text/css">
/* set up the keyframes; remember to create prefixed keyframes too! */
#keyframes nodeInserted {
from { opacity: 0.99; }
to { opacity: 1; }
}
#parent > button {
animation-duration: 0.001s;
animation-name: nodeInserted;
}
</style>
#parent is the id for the div, in which i am going to append button's
dynamically.
<script type="text/javascript">
var insertListener = function(event){
if (event.animationName == "nodeInserted") {
// This is the debug for knowing our listener worked!
// event.target is the new node!
console.warn("Another node has been inserted! ", event, event.target);
}
}
document.addEventListener("animationstart", insertListener, false); // standard + firefox
document.addEventListener("MSAnimationStart", insertListener, false); // IE
document.addEventListener("webkitAnimationStart", insertListener, false); // Chrome + Safari
setInterval(function(){
var btn = document.createElement("BUTTON");
var t = document.createTextNode("CLICK ME"); // Create a text node
btn.appendChild(t);
document.getElementById("parent").appendChild(btn);
}, 2000);
</script>
This is rather difficult to accomplish, because there is no viable event for reacting to DOM changes. I would rather stick to event delegation instead.
But there are some events that you may find useful or interesting. On Mozilla Developer Network's list of DOM events you can see eg.:
DOMNodeInserted,
DOMNodeInsertedIntoDocument,
DOMNodeRemoved,
DOMElementNameChanged,
All of them however are marked as W3C drafts.
A generic way to detect node insertion is to use the DOMNodeInserted mutation event.
I am interested in every node actually. I am changing the colors of everything on the page.
For this purpose, a better solution is to inject a dynamic stylesheet, sufficed with the !important flag. If you only want to change colors, I recommend the Stylish extension (Stylish for Chrome) instead of GreaseMonkey.
Related
How would one fire an event that fires every time a div is created?
I was thinking something along the lines of this, but clearly not.
$("#content").on("create", "div.block", function () {
$(this).css({"background-color":"#FF0000"});
});
I have discovered MutationObserver (https://stackoverflow.com/a/11546242/165737), however as seen in the comments this does not work in IE.
you could use a mutation event (DOMNodeInserted)... however IE might not fully capable of dealing with that.
http://jsfiddle.net/kasperfish/AAd8f/
$(function() {
$('#btn').click(function(){
$('body').append('<div style="width:30px;height:30px;border:1px solid black"></div>');
});
$(document).on('DOMNodeInserted', function(e) {
$(e.target).addClass('blue');
});
});
I like to note that it is better to use callback events instead of listening to the DOM. You'll need to write a callback after a div is inserted in the DOM. I don't think there is an other (non-hacky) way to accomplish this.
You may try something like this instead of MutationObserver to get notified when ever a div.block has been added to the div#content
$(function(){
var c = $('#content'), l = c.find('.block').length;
setInterval(function(){
var lOld = l;
if(c.find('.block').length > lOld) {
l = c.find('.block').length;
c.find('.block').css({"background-color":"#FF0000"});
}
}, 100);
});
An example and here is another example with multiple Background color effect.
I was trying to use querySelector to find active elements in a page. I assumed that a handler bound to a mousedown event on the document would trigger after the event had bubbled its way back from the target, which means the :active pseudoclass should already be applied.
document.addEventListener("mousedown",function(e){
console.log(document.querySelector("*:active"));// logs null
// expected value was the target of the mousedown event, that is,
console.log(e.target);
});
My question is: at what point exactly does the the :active pseudo-class apply? Note that when I log the value, the mousedown event has already triggered on the target.
See http://jsfiddle.net/tK67w/2/ for an example. An interesting thing to note here is that if you set a breakpoint within the handler, you can see the css rule I defined for a:active already applying, although querySelector is returning null
EDIT:
Credit goes to TJ for coming up with a much better demonstration. The problem still stands though: in browsers other than IE and Chrome, how can I get an HTMLCollection of all active elements as they become active?
I believe the issue is that as you're using querySelector, you're only getting the first active element. But your anchor is much further down the tree.
Update: Interesting, I'm not getting anything with Firefox or Opera, but I am with Chrome. The below are Chrome results. See more on that below.
Consider (live copy):
document.addEventListener("mousedown", handler, false);
function handler(e){
console.log(
"event " + e.type + ": " +
Array.prototype.join.call(document.querySelectorAll("*:active")));
}
When I click the anchor, I see this in the console:
event mousedown: [object HTMLHtmlElement],[object HTMLBodyElement],[object HTMLDivElement],http://fiddle.jshell.net/_display/#
Note the URL at the end, which is the default toString for HTMLAnchroElement instances, which is triggered by the join.
Since querySelectorAll is required to return the elements in document order, if you want the most specific active element, you'd use the last one. Example (live copy):
(function() {
document.addEventListener("mousedown",handler, false);
function handler(e){
var active = document.querySelectorAll("*:active");
var specific = active && active.length && active[active.length-1];
display("Most specific active element: " +
(specific ? specific.tagName : "(none)"));
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
In my case (using Chrome), I see the tag name of the most specific element (the anchor if I click the link, etc.).
It seems like Chrome is following the spec and taht Firefox and Opera are not. From Section 6.6.1.2 of the CSS3 Selectors spec:
The :active pseudo-class applies while an element is being activated by the user. For example, between the times the user presses the mouse button and releases it.
It seems to me that :active should therefore apply in the above. This assertion is backed up if we use this CSS:
*:active {
background-color: red;
}
...with this JavaScript:
(function() {
document.addEventListener("mousedown", mouseDown, false);
document.addEventListener("mouseup", mouseUp, false);
function mouseDown(){
var active = document.querySelectorAll("*:active");
var specific = active && active.length && active[active.length-1];
display("Mouse down: Most specific active element: " +
(specific ? specific.tagName : "(none)"));
}
function mouseUp() {
display("Mouse up");
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
Live Copy
With all three browsers I tried (Chrome, Firefox, Opera), the element gets a red background while the mouse is down, and goes white again when I release it; but the mousedown handler doesn't see the :active element in Firefox or Opera, just Chrome.
But I'm not a CSS specification lawyer. :-)
Seems to be set after the frame has been rendered, or maybe after the current execution queue, that's at least the case with Firefox.
Got results using setTimeout without delay (works with requestAnimationFrame too):
document.addEventListener('mousedown', e => {
setTimeout(_ => console.log(document.querySelectorAll(':active')));
});
Quick Question:
Anyone knows this? :
http://lwis.net/free-css-drop-down-menu/
It uses javascript for -IE6 only, but with javascript frameworks.
Is it possible to run this without a framework?
It could be used without a js library, but not out of the box. You'd have to write the js yourself. All you need is basically "on hover, set the style to display the dropdowns", and "on mouseout, set the stlye to hide the dropdowns." The key for that is the attachEvent function (for IE only, so if you decide you need other browsers, you'll need a modify the code here). It's better to stick it in a function so it's easier to work with:
function observe(target, eventType, func, useCapture) {
target = (typeof target=="string") ? document.getElementById(target) : target;
useCapture = useCapture || false;
target.attachEvent("on"+eventType, function(event) {func(event, target)});
}
You could then use that function to wait for page load, then set up the callbacks for the hover and mouseout:
observe(window, "load", function() {
observe("button1", "mouseover", function() {
document.getElementById("dropdown1").style.display = "block";
});
observe("button1", "mouseout", function() {
document.getElementById("dropdown1").style.display = "none";
});
});
This is of course a very simplistic example, only using id's to grab your menu buttons and dropdowns, but it gives you an idea how to get it started.
It dosn't need a javascript frameworks its css based, it dosnt even need javascript enabled for it to work
n.parentNode.firstChild.nextSibling.firstChild.nextSibling.style.visibility = "visible"
I have an onClick event to change style. How to change style back when a user clicks elsewhere of an item?
Both prototype and scriptaculous libraries included.. many of the below answers doesn't work with them... Also ID of an element is UNDEFINED so it can't be used for reference in javascript.
Thanks, Yan
I have not tested this in all browsers, but if you don't want to introduce any new js framework, this solution only use CSS:
<ul>
<li tabindex="1">First</li>
<li tabindex="2">Second</li>
</ul>
The property tabIndex makes the li element focusable. And the css:
li { color: #F00; }
li:focus { color: #0F0 }
This is of course very basic styling, probably want to put it in classes or whatever.
Great question! You can use "event bubbling", which means that instead of the onclick event on your element, you define an event handler on a higher object (say, document or a table), and there you say something like:
if (event.target === myElement) {
changeStyle();
} else {
changeStyleBack();
}
More here (and elsewhere): http://www.southsearepublic.org/tag/Javascript%20Event%20Bubbling/read
When an item is clicked on it, it gains focus. When something else is clicked on it will lose focus, triggering the onblur event. May not work for all elements, but it would work for, say, <input> elements.
You want the onblur event: "The onblur event occurs when an object loses focus".
You can bind an onClick event on the body and assign the function that restore the style to that event.
There's a live example at http://jsbin.com/oxobi3
I would use jQuery live event and bind click event using :not selector
Maybe try onclick="function1()" onblur="function2()" or onfocus="function1()" onblur="function2()" in the tag.
Here's how you could do it in jQuery:
$(document).click(function(e) {
if ($(e.target).is("#specialItem")) {
$(e.target).addClass("specialClass");
} else {
$(#specialItem").removeClass("specialClass");
}
});
If you're not using jQuery, you can still use the basic model -- apply the onclick event logic at the document level. This will work for items that don't respond to the blur event.
It's been quite a long time since I've used prototype but I hope this helps you (in the non-jQuery sense.)
$(window).observe('click', respondToClick);
function respondToClick(event) {
var element = event.element();
if(!($(this) == element)){
element.addClassName('active');//or do your stuff here
}
}
My approach
var elsewhere = 1;
$(myelement).bind('hoverin',function(){
elsewhere = 0;
});
$(myelement).bind('hoverout',function(){
elsewhere = 1;
});
$('screenarea').click(function(){
if(elsewhere){
change-style-back();
} else{
change-style();
}
});
this will make sure that when you click somewhere on screen and its not on your element, then the style will change back
Is there a way to detect if the "display" css property of an element is changed (to whether none or block or inline-block...)? if not, any plugin? Thanks
Note
Mutation events have been deprecated since this post was written, and may not be supported by all browsers. Instead, use a mutation observer.
Yes you can. DOM L2 Events module defines mutation events; one of them - DOMAttrModified is the one you need. Granted, these are not widely implemented, but are supported in at least Gecko and Opera browsers.
Try something along these lines:
document.documentElement.addEventListener('DOMAttrModified', function(e){
if (e.attrName === 'style') {
console.log('prevValue: ' + e.prevValue, 'newValue: ' + e.newValue);
}
}, false);
document.documentElement.style.display = 'block';
You can also try utilizing IE's "propertychange" event as a replacement to DOMAttrModified. It should allow to detect style changes reliably.
You can use attrchange jQuery plugin. The main function of the plugin is to bind a listener function on attribute change of HTML elements.
Code sample:
$("#myDiv").attrchange({
trackValues: true, // set to true so that the event object is updated with old & new values
callback: function(evnt) {
if(evnt.attributeName == "display") { // which attribute you want to watch for changes
if(evnt.newValue.search(/inline/i) == -1) {
// your code to execute goes here...
}
}
}
});
You can use jQuery's css function to test the CSS properties, eg. if ($('node').css('display') == 'block').
Colin is right, that there is no explicit event that gets fired when a specific CSS property gets changed. But you may be able to flip it around, and trigger an event that sets the display, and whatever else.
Also consider using adding CSS classes to get the behavior you want. Often you can add a class to a containing element, and use CSS to affect all elements. I often slap a class onto the body element to indicate that an AJAX response is pending. Then I can use CSS selectors to get the display I want.
Not sure if this is what you're looking for.
For properties for which css transition will affect, can use transitionend event, example for z-index:
$(".observed-element").on("webkitTransitionEnd transitionend", function(e) {
console.log("end", e);
alert("z-index changed");
});
$(".changeButton").on("click", function() {
console.log("click");
document.querySelector(".observed-element").style.zIndex = (Math.random() * 1000) | 0;
});
.observed-element {
transition: z-index 1ms;
-webkit-transition: z-index 1ms;
}
div {
width: 100px;
height: 100px;
border: 1px solid;
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="changeButton">change z-index</button>
<div class="observed-element"></div>
You can't. CSS does not support "events". Dare I ask what you need it for? Check out this post here on SO. I can't think of a reason why you would want to hook up an event to a style change. I'm assuming here that the style change is triggered somwhere else by a piece of javascript. Why not add extra logic there?