After a long struggle, I've finally found the only way to clear autofill styling in every browser:
$('input').each(function() {
var $this = $(this);
$this.after($this.clone()).remove();
});
However, I can’t just run this in the window load event; autofill applies sometime after that. Right now I’m using a 100ms delay as a workaround:
// Kill autofill styles
$(window).on({
load: function() {
setTimeout(function() {
$('.text').each(function() {
var $this = $(this);
$this.after($this.clone()).remove();
});
}, 100);
}
});
and that seems safe on even the slowest of systems, but it’s really not elegant. Is there some kind of reliable event or check I can make to see if the autofill is complete, or a cross-browser way to fully override its styles?
If you're using Chrome or Safari, you can use the input:-webkit-autofill CSS selector to get the autofilled fields.
Example detection code:
setInterval(function() {
var autofilled = document.querySelectorAll('input:-webkit-autofill');
// do something with the elements...
}, 500);
There's a bug open over at http://code.google.com/p/chromium/issues/detail?id=46543#c22 relating to this, it looks like it might (should) eventually be possible to just write over the default styling with an !important selector, which would be the most elegant solution. The code would be something like:
input {
background-color: #FFF !important;
}
For now though the bug is still open and it seems like your hackish solution is the only solution for Chrome, however a) the solution for Chrome doesn't need setTimeout and b) it seems like Firefox might respect the !important flag or some sort of CSS selector with high priority as described in Override browser form-filling and input highlighting with HTML/CSS. Does this help?
I propose you avoiding the autofill in first place, instead of trying to trick the browser
<form autocomplete="off">
More information: http://www.whatwg.org/specs/web-forms/current-work/#the-autocomplete
If you want to keep the autofill behaviour but change the styling, maybe you can do something like this (jQuery):
$(document).ready(function() {
$("input[type='text']").css('background-color', 'white');
});
$(window).load(function()
{
if ($('input:-webkit-autofill'))
{
$('input:-webkit-autofill').each(function()
{
$(this).replaceWith($(this).clone(true,true));
});
// RE-INITIALIZE VARIABLES HERE IF YOU SET JQUERY OBJECT'S TO VAR FOR FASTER PROCESSING
}
});
I noticed that the jQuery solution you posted does not copy attached events. The method I have posted works for jQuery 1.5+ and should be the preferred solution as it retains the attached events for each object. If you have a solution to loop through all initialized variables and re-initialize them then a full 100% working jQuery solution would be available, otherwise you have to re-initialize set variables as needed.
for example you do: var foo = $('#foo');
then you would have to call: foo=$('#foo');
because the original element was removed and a clone now exists in its place.
Related
https://jsfiddle.net/1vm0259x/ I want to have it so when the contents of #b changes it immediately makes the div display. Is that possible? I'm having to get a little hacky because of the limitations of a CMS plugin. I don't know jQuery very well.
Markup
<div id="a">
<span id="b">0 items</span>
</div>
jQuery
$(document).ready(function(){
if($('#b:contains("0 items")')) {
$('#a').css("display", "none");
}
if($('#b:not(:contains("0 items"))')) {
$('#a').css("display", "block");
}
});
The best way to monitor for text changes like this are to find a way to hook into some related and existing event in the browser and then see if the text has changed to what you want.
In the newer browsers (such as IE11+, recent versions of Chrome, Firefox and Safari), you can use a DOM MutationObserver to directly watch to see if the text nodes change.
The mutation callback is called anytime the children of the specified element are changed (children include the text nodes).
Here's some runnable code that watches for a text change in a div in this code snippet:
document.getElementById("go").addEventListener("click", function(e) {
var t = document.getElementById("test");
t.innerHTML = parseInt(t.innerHTML, 10) + 1;
});
var m = new MutationObserver(function(mRecords, obj) {
log("Current text value: " + document.getElementById("test").innerHTML);
}).observe(document.getElementById("test"), {childList: true, characterData: true, subtree: true});
function log(x) {
var d = document.createElement("div");
d.innerHTML = x;
document.body.appendChild(d);
}
<button id="go">Click Me to Change the Text</button><br><br>
<div id="test">1</div><br><br>
If you need support for older version of IE, then the next best thing would be to figure out what existing events in the browser precede the text change and monitor those events. When one of those events occurs, you can then check the text. For example, if the action that triggers the text change always comes after an Ajax call, you can monitor/hook Ajax calls on a system wide basis and then check your text after each Ajax call completes. Since this only ever does anything when other things are already happening in the web page, it's very efficient. Or, if the text only changes after a particular button is clicked or some text field is changed, you can monitor those DOM elements with event listeners.
To suggest how to do that more specifically, we'd need to see the details of your actual circumstance and would have to understand what events in the page lead to the changing text. Such short duration timers can also negatively affect the performance of things like animations running in your page or in other tabs.
It is NOT recommended to use a short duration timer to poll the DOM because this kills mobile battery life and, in fact, mobile browsers will attempt to delay or slow down any long running interval timers you use in order to try to preserve battery life.
On top of MutationObserver, eloquently put by #jfriend00, there is an older API available at our disposal as well by the name DOMSubtreeModified. Combine that with onpropertychange that of Internet Explorer and I believe you get a nice backward compatible change event. Take a look at the snippet below, not thoroughly tested though:
Snippet:
var myDIV=document.getElementById('a');
var mySpan=document.getElementById('b');
var myButton=document.getElementById('button');
var myResult=document.getElementById('result');
if(window.addEventListener){
myButton.addEventListener('click',onButtonClicked,false);
mySpan.addEventListener('DOMSubtreeModified',onSpanModified,false);
}else{
myButton.attachEvent('onclick',onButtonClicked);
mySpan.attachEvent('onpropertychange',onSpanModified);
}
function onButtonClicked(){
//mySpan.innerText=Math.random();
mySpan.innerHTML=Math.random();
}
function onSpanModified(){
myResult.innerHTML=mySpan.innerHTML;
}
<div id="a">
<span id="b">0</span>
</div>
<input id="button" type="button" value="Click Me" />
<span id="result"></span>
Hope this helps in some way though. Apologies if this was not what you were looking for and if I misunderstood your problem completely.
You can't really "watch" something in JavaScript since it's an event-driven language (there are a few exceptions to this rule when it comes to object properties, but it is not something that is commonly useful).
Instead you should set an interval that updates regularly. I used 50 millisecond intervals, you can choose to use whatever interval you like:
function update() {
if($('#b:contains("0 items")').length) {
$('#a').css("display", "none");
}
if($('#b:not(:contains("0 items"))').length) {
$('#a').css("display", "block");
}
}
$(document).ready(function () {
setInterval(update, 50);
});
I'm working on a widget for a web application. When the user selects something in the widget, I use a CSS animation to hide it, then change something in the page based on the selection. I use the animationend event to wait before proceeding.
The widget can have multiple themes, meaning someone might choose not use a CSS animation. I would like to separate logic and presentation as much as possible, so I would prefer to avoid making other themes use JS.
How can I handle the fact that I don't know if an animation is present?
I was hoping to do something like the following, but I can't find anything like isAnimating in documentation.
elem.classList.add('hide');
if(elem.isAnimating()) {
elem.addEventListener('animationend', callback);
} else {
callback();
}
You can use the animationstart event.
var anmtn = false;
, switcher = function(){anmtn = !anmtn})
elem.addEventListener('animationstart', switcher);
elem.addEventListener('animationend', switcher);
//Poll for animation.
var e = setInterval(function() {
if( anmtn )
//code
},62)
That should work. Untested!
For a more dapper polling use:
http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery
MDN: https://developer.mozilla.org/en-US/docs/Web/Events/animationstart.
I am trying to do detect element resize with jquery resize plugin (http://benalman.com/projects/jquery-resize-plugin/) on jquery 1.10.2.
$("#element").resize(function(){
console.log("resize");
});
I did the testing on Firefox 25 and I get this error:
Error: TypeError: r is undefined
Source File: jquery.ba-resize.min.js
Line: 9
How can I solve it? Is there any alternative way / plugin for doing this?
Thank you.
You need a resize sensor which is bundled with css-element-queries of https://github.com/marcj/css-element-queries.
new ResizeSensor($('.elements'), function(){
console.log('resize')
});
jQuery's .resize only works on window object since only window has a event onresize. All other element haven't and thus you need a polyfill for that. I've seen a lot of other jQuery plugins that allow you to listen on resize changes of all element types, but take care: These are incredible slow as they use setTimeout() or interval to check it's dimension change instead of setting up a real resize sensor like the one you can find in the link above.
Using Clay.js (https://github.com/zzarcon/clay) it's quite simple to detect changes on element size:
var el = new Clay('.element');
el.on('resize', function(size) {
console.log(size.height, size.width);
});
DIV does not fire a resize event, so you won't be able to do exactly what you've coded, but you could look into monitoring DOM properties.
If you are actually working with something like resizables, and that is the only way for a div to change in size, then your resize plugin will probably be implementing a callback of its own.
Remove that plug-in, jQuery done with it self.
var element = $('#element');
var current_size = element.width()+'x'+element.height();
element.resize(function(){
var this_size = $(this).width()+'x'+$(this).height();
if(current_size!==this_size){
console.log('changed');
current_size = this_size;
}
});
PS : Doesn't test
The following is a simple test case to demonstrate what I'm trying to do:
<html>
<head>
<title>Test</title>
</head>
<body>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function()
{
$(":target").css('color', 'red');
});
</script>
<ul>
<li id="one">One</li>
<li id="two">Two</li>
<li id="three">Three</li>
</ul>
</body>
</html>
The idea is to do something through jQuery/Javascript to the targetted item, when something is targetted (for example, test.html#two).
This works as I expect it to in Firefox and IE 10, but not in Chrome, Opera or Safari, which leaves me wondering if this is a bug in some browsers, if what I'm trying to do is somehow wrong, or if I've run afoul of an inadequately precise part of some specification or other.
If I change the jQuery code to do
alert($(":target").length);
it becomes apparent that Chrome, Opera and Safari can't find the :target element during document.ready(), but calling the same code later (via console or function attached to a click event) does find the elements.
When should :target become accessible to JS?
This was posted as a comment but was later removed, you can try waiting for the window load event:
$(window).on('load hashchange', function(){
$(':target').css('color', 'red');
});
This for me produced mixed results on Chrome, it worked when doing a page refresh (F5) but not when hitting enter in the address bar.
I don't know if there's any way to handle this correctly on page load using the :target selector but you could always get the hash value and use it as your selector:
$(window).on('load hashchange', function(){
var target = window.location.hash;
$(target).css('color', 'red');
});
UPDATE
I've been doing some research on the issue plus some tests and I have a couple of insights to share:
First off, we need to understand that when calling $(':target') jQuery internally makes use of querySelectorAll(':target') which means it's directly related to the CSS specification of the pseudo-class, but why isn't working inside document.ready()?
Well, I found that wrapping the code inside setTimeout(fn,0) actually makes the selector available:
$(document).ready(function(){
setTimeout(function(){
$(':target').css('color', 'red'); //THIS WORKS
},0);
});
You can read this answer for an explanation on how adding a zero-ms timeout actually makes a difference, but basically it allows the browser to complete other non-javascript related tasks (in which we would find making the actual CSS pseudo-class available for query). I believe Firefox somehow manages its internal processes differently and that's why the code works there without the need for a timeout.
Now I also discovered that jQuery's internal sizzle selector engine provides a fallback for browsers that do not support CSS :target pseudo-class, which you can use inside document.ready() without issue:
$(document).ready(function(){
$(':target()').css('color', 'red');
});
This works because instead of relying on the CSS class it is a javascript implementation that makes use of the hash property on the window.location object, internally it is defined as follows:
"target": function( elem ) {
var hash = window.location && window.location.hash;
return hash && hash.slice( 1 ) === elem.id;
}
The only think you should note is that this function will go through every element on the page if it's not passed a selector like :target(div), so I believe using the workaround I provided earlier would still be a better option than this.
Because the page is not been reloaded. You need bind it to hashchange:
$(window).on('hashchange', function(){
$(":target").css('color', 'red');
});
http://jsfiddle.net/sXsYx/
Notice that you have much more work to make it right, maybe combine it with $(document).ready
you can use css3 target selector for styling work
:target
{
color:red;
}
Since there is no specialized logic in your example (if statements or such), why don't you just do the styling in CSS? The :target pseudo-class is a CSS3 selector.
:target {
color: red;
}
Note that this will work in all modern browsers, and even some very old browsers (Chrome 1 and Firefox 1.3, for instance), but with Internet Explorer it is only supported starting from version 9.
You can also do it in both places if you wish (CSS and JavaScript), however the JavaScript would seem redundant unless you specifically need IE <= 8 compatibility.
I've noticed that you are using jQuery version 1.10.1, which retains support for IE <= 8. Is that important? If not you can also move to jQuery 2.0.2 (latest version at time of writing).
You should do like this
$("li:target")
This will select the element. This is the better way
Refer this please,
http://api.jquery.com/target-selector/
Or you should remove document ready and put the script at the end of the html document
I am writing a script that needs to detect elements added to a Web page, for example events rendered in a calendar (div tags). I don't care about elements that are removed. There should be at most 20-30 such elements on the page.
My idea - short and easy code - is to use a specific class ("myName") to brand elements already in the page. At regular intervals I would poll the page:
// Get all divs in the calendar:
var allDivsCount=myCalendar.querySelectorAll("div").length;
// Get already branded divs
var oldDivsCount=myCalendar.querySelectorAll("div.myName").length;
if (allDivsCount > oldDivsCount) {
// brand the new divs and do stuff
}
Is this a good practice, or is there a better way to do it? Are there libraries that already have such logic implemented?
I am trying to avoid DOMNodeInserted as some browsers don't support it and it is deprecated (due to performance issues, from what I've read).
I know that you're against DOMNodeInserted but I'll added it anyways for options (in case you're not supporting older version of IE).
I blogged about this a while back but it seems like the same solution still applies today (but like I said, depending on the browsers you currently support).
Example:
$(document.body).on('DOMNodeInserted', function (e) {
if (e.currentTarget.toString() === 'HTMLBodyElement') {
console.log(e);
}
});
This triggers any changes within the body not just the body itself.
$('#context').append($('<div />')); // triggers the event above
If you can intercept a DOM change (an AJAX call for instance) and create a global callback function, that would be ideal instead of the setInterval option.
var globalCallback = function() {
/** Do something when the DOM changes */
};
/** Global AJAX event to watch for all AJAX complete within the body */
$('body').ajaxComplete(globalCallback);
This is just one example (AJAX callbacks) of course.