How to check if css box-shadow is supported (jQuery)? - javascript

I'm creating a layout in full css. However, some browser (such as IE6) do not support box-shadow (.. and -webkit-box-shadow or -moz-box-shadow). I would like to check if it's not supported and then add other styles.
How's this possible in jQuery?
Martti Laine

var check = document.createElement('div');
var shadow = !!(0 + check.style['MozBoxShadow']);
if(shadow)
alert('moz-box-shadow available');
That is the doing-it-yourself way. Other reliable way is the modernizr library, which does feature detection for you.
http://www.modernizr.com/
No jQuery needed at all here.

A neat function (pure JavaScript, no jQuery) to check which CSS features are supported by the browser is described in Quick Tip: Detect CSS3 Support in Browsers with JavaScript.
Function is as follows:
var supports = (function() {
var div = document.createElement('div'),
vendors = 'Khtml Ms O Moz Webkit'.split(' '),
len = vendors.length;
return function(prop) {
if (prop in div.style) {
return true;
}
prop = prop.replace(/^[a-z]/, function(val) {
return val.toUpperCase();
});
while (len--) {
if (vendors[len] + prop in div.style) {
// browser supports box-shadow. Do what you need.
// Or use a bang (!) to test if the browser doesn't.
return true;
}
}
return false;
};
})();
Usage is like this:
if (supports('boxShadow')) {
// Do whatever
}
It worked like a charm for me! :-)

Related

Testing multiple browsers with protractor backed by page objects

I'm writing a test where two browsers need to interact. The problem with simply forking the browser is that my page objects still reference the old browser. I didn't want to rewrite all of my PO's to take the browser as a parameter so I tried the first solution found in the link below where they overwrite the global variables with the new browser's version :
Multiple browsers and the Page Object pattern
However, changing the global variables doesn't seem to work as all the subsequent page object functions that I call are performed against the original browser instance. I have tried logging the window handler before and after the switch and they are indeed different which only baffles me further. Here's some of the code.
spec:
var MultiBrowserFunctions = require('../common/multiBrowserFunctions.js');
var HomePage = require('../home/home.po.js');
describe('blah', function(){
it('blah', function(){
MultiBrowserFunctions.openNewBrowser(true);
HomePage.initializePage();
});
});
MultiBrowserFunctions:
(function() {
var browserRegistry = [];
module.exports = {
openNewBrowser: function(isSameUrl){
if(typeof browserRegistry[0] == 'undefined'){
browserRegistry[0] = {
browser: browser,
element: element,
$: $,
$$: $$,
}
}
var tmp = browser.forkNewDriverInstance(isSameUrl);
var id = browserRegistry.length;
browserRegistry[id] = {
browser: tmp,
element: tmp.element,
$: tmp.$,
$$: tmp.$$,
}
switchToBrowserContext(id);
return id;
},
resetBrowserInstance : function(){
browserRegistry.splice(1,browserRegistry.length);
switchToBrowserContext(0);
}
}
function switchToBrowserContext(id){
console.log('---------------------------switching to browser: ' + id);
browser=browserRegistry[id].browser;
element=browserRegistry[id].element;
$=browserRegistry[id].$;
$$=browserRegistry[id].$$;
}
}());
My questions are:
(1) why doesn't this work?
(2) Is there some other solution that doesn't involve rewriting all of my po's?
What you can do is, save the browsers in different variables and then switch between them by overriding the globals via a utility or something.
describe('Switching browsers back and forth', function () {
var browserA, browserB;
it('Browser Switch', function () {
var browsers = {
a : browser,
b : browser.forkNewDriverInstance(true)
};
browserA = browsers.a;
browserB = browsers.b;
var browserAndElement = switchBrowser(browserB);
browser = browserAndElement.browser;
element = browserAndElement.element;
//do your stuff
var browserAndElement = switchBrowser(browserA);
browser = browserAndElement.browser;
element = browserAndElement.element;
//do your stuff
});
});
The switchBrowser() can look like following:
this.switchBrowser = function (currentBrowser) {
browser = currentBrowser;
element = currentBrowser.element;
return {
browser : browser,
element : element
}
}
In this way you don't have to rewrite your POs to take in the new globals.
Hope it helps!
Cheers

jQuery .hasClass() method fails for SVG elements

I have a set of SVG elements with the classes node and link. My program should detect whether an element has the node class or the link class upon hovering over any of the SVG elements. However, for some reason, the .hasClass() doesn't seem to work:
$(".node").hover(function(evt){
console.log($(this).attr("class")); //returns "node"
console.log($(this).hasClass('node')); //returns false
}, function(){console.log("Done");});
So the element I hovered on has the class node, and jQuery detects that too, as shown by console.log($(this).attr("class"));, but for some reason the actual .hasClass() fails. Why is this? Is it failing because of the SVG?
The class attribute for HTML element doesn't have the same meaning in SVG.
$("<b></b>").addClass($(this).attr("class")).hasClass("node")
Or
/(^|\s)node(\s|$)/.test($(this).attr("class"))
for SVG elements.
EDIT .hasClass seems to work just fine (at least in IE9 and FF) http://jsfiddle.net/X6BPX/1/
So the problem could be any combination of the following: a syntax error, using an outdated browser, using an outdated version of jQuery.
As Bergi pointed out in comments, jQuery silently fails on SVG elements on account of className returning an SVGAnimatedString object instead of a normal DOMString.
See this JSFiddle for a comparison.
I was tempted to submit a pull request on this, but did a quick project search, and apparently the jQuery project stance on SVG issues is wontfix: https://github.com/jquery/jquery/pull/1511
If you're using D3, you could use d3.select(this).classed('node'). Note that D3 correctly returns for both HTML elements and SVG elements.
This is not the fastest option ever, but it is a possible solution. Instead of using jQuery's hasClass you could instead obtain the class attribute as a string and use indexOf to search through it. There are probably use cases where this will fail, so I wouldn't recommend this except for super simple projects.
Working example:
var s = $(this).attr('class');
if( s.indexOf('node')!==-1 ){
// do something
}
Remember: indexOf returns -1 when it can't find anything, not 0. 0 is returned when the substring starts at index 0.
This is a hack for addClass, removeClass, hasClass jquery methods for before jquery 3.x.x versions.
$.fn.extend({
addSVGClass: function (cls) {
return this.each(function () {
var classList = $(this).attr('class');
if (classList) {
var classListArr = classList.split(" ");
if (classListArr.indexOf(cls) === -1) {
classListArr.push(cls);
classList = classListArr.join(" ").trim();
$(this).attr('class', classList);
}
} else {
$(this).attr('class', cls);
}
});
},
removeSVGClass: function (cls) {
return this.each(function () {
var classList = $(this).attr('class');
if (classList) {
var classListArr = classList.split(" ");
if (classListArr.indexOf(cls) !== -1) {
delete classListArr[classListArr.indexOf(cls)];
classList = classListArr.join(" ").trim();
$(this).attr('class', classList);
}
}
});
},
hasSVGClass: function (cls) {
var el = this[0];
var classList = $(el).attr('class');
if (classList) {
var classListArr = classList.split(" ");
if (classListArr.indexOf(cls) !== -1) {
return true;
} else {
return false;
}
}
return false;
}
});
usage :
$('.svg-element').addSVGClass('selected');
Works. But be sure to close the function
$(".node").hover(function(evt){
console.log($(this).attr("class")); //returns "node"
console.log($(this).hasClass('node')); //returns false
}, function(){console.log("Done");});
http://jsfiddle.net/X6BPX/

JavaScript Event prototype in IE8

I'm trying to add a method to the Event prototype. In order to call/set preventDefault() or, in IE-speak returnValue = false and -if desired- stopPropagation() / cancelBubble = true;. I thought the code below would have sufficed.
Event = Event || window.Event;
//^^ makes the fiddle work on IE8 ^^
if(!(Event.prototype.stopEvent))
{
Event.prototype.stopEvent = function(propagate)
{
"use strict";
propagate = (propagate ? true : false);
if (this.preventDefault)
{
this.preventDefault();
if (propagate === false)
{
this.stopPropagation();
}
}
else
{
this.returnValue = false;
this.cancelBubble = !propagate;
}
return this;
};
}
Which seems to work, as you can see here. This fiddle shows OK in IE8, firefox and chrome. Though, when I add this to my script, IE8 breaks on the first line, saying 'Event is undefined'. Leaving out "use strict"; makes no difference at all.
Reluctantly, I tried this, too:
if (typeof Event === 'undefined')
{
var Event = window.Event || window.event;//FFS IE :-(
}
But to no avail: Error: 'Event.prototype' is null or not an object, so I got 1 line further. The thing is, the entire prototype method is a copy paste from my script, but what am I overlooking here? Any idea's/suggestions? Thanks
PS: I like Pure JavaScript, so please, don't suggest jQuery, prototypejs, dojo,... as a solution. I've just gotten rid of jQuery. (I like jQuery, but there is no need for it in this case)
Update
Things have taken a turn for the worse, I'm afraid. I found this MSDN reference. The entire page deals with DOM Element prototypes. It's pretty fair to say they are available and usable in IE8 (to some extent). On this page, this code caught my eye:
Event.prototype.stopPropagation = function ()
{
this.cancelBubble = true;
};
Event.prototype.preventDefault = function ()
{
this.returnValue = false;
};
It can be found ~3/4ths of the page down, in the section titled "Powerful Scenarios". This is, to my mind exactly the same thing as I want to do, but what's more: if I try this code via jsfiddle, it doesn't even work, whereas my jsfiddle (with my code) did work on IE8. This is just the last few lines of a snippet, but as far as I can work out, these few lines of code should work just fine. I've altered them as follows:
Event.prototype.stopPropagation = function ()
{
if (this.stopPropagation)
{
return this.stopPropagation();
}
this.cancelBubble = true;
};
Event.prototype.preventDefault = function ()
{
if (this.preventDefault)
{
return this.preventDefault();
}
this.returnValue = false;
};
I recently had (another) brainwave, and I think I found a better way of augmenting the event prototype. Strictly speaking, the Event prototype is not accessible in IE (<9), but it is, I found out accessible if you work your way back from an instance (well, the instance: window.event). So here's a snippet that works in all major browsers (and IE8 - not 7):
(function()
{
function ol(e)
{//we have an event object
e = e || window.event;
if (!e.stopEvent)
{
if (Object && Object.getPrototypeOf)
{//get the prototype
e = Object.getPrototypeOf(e);
}
else
{//getting a prototype in IE8 is a bit of a faff, this expression works on most objects, though
//it's part of my custom .getPrototypeOf method for IE
e = this[e.constructor.toString().match(/(function|object)\s+([A-Z][^\s(\]]+)/)[2]].prototype;
}
e.stopEvent = function(bubble)
{//augment it (e references the prototype now
bubble = bubble || false;
if (this.preventDefault)
{
this.preventDefault();
if (!bubble)
{
this.stopPropagation();
}
return this;
}
this.returnValue = false;
this.cancelBubble = !bubble;
return this;
};
}
alert(e.stopEvent ? 'ok' : 'nok');//tested, it alerts ok
if (this.addEventListener)
{
this.removeEventListener('load',ol,false);
return;
}
document.attachEvent('onkeypress',function(e)
{
e = e || window.event;
if (e.stopEvent)
{//another event, each time alerts ok
alert('OK!');
}
});
this.detachEvent('onload',ol);
}
if (this.addEventListener)
{
this.addEventListener('load',ol,false);
}
else
{
this.attachEvent('onload',ol);
}
})();
That way, the header doctype doesn't matter all that much: I've tested it using the <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">, and it works in FF, chrome and IE 8, no problems whatsoever. Using <!DOCTYPE html> to be safe, though
Hope this helps someone...
Its Standards versus Quirks mode. The JSFiddle page has a DOCTYPE declaration, albeit an incredibly simple one, <!DOCTYPE html>, that kicks the render into Standards mode. Chances are your web page does not have a DOCTYPE which leaves the render in Quirks mode. After adding that simple DOCTYPE to a page I built from your fiddle, it worked for me.

How to detect if browser support specified css pseudo-class?

What's concept of detecting support of any css pseudo-class in browser through JavaScript? Exactly, I want to check if user's browser supports :checked pseudo-class or not, because I've made some CSS-popups with checkboxes and needs to do fallbacks for old browsers.
ANSWER: I'm found already implemented method of testing css selectors in a Modernizr "Additional Tests".
You can simply check if your style with pseudo-class was applied.
Something like this: http://jsfiddle.net/qPmT2/1/
For anyone still looking for a quick solution to this problem, I cribbed together something based on a few of the other answers in this thread. My goal was to make it succinct.
function supportsSelector (selector) {
const style = document.createElement('style')
document.head.appendChild(style)
try {
style.sheet.insertRule(selector + '{}', 0)
} catch (e) {
return false
} finally {
document.head.removeChild(style)
}
return true
}
supportsSelector(':hover') // true
supportsSelector(':fake') // false
stylesheet.insertRule(rule, index) method will throw error if the rule is invalid. so we can use it.
var support_pseudo = function (){
var ss = document.styleSheets[0];
if(!ss){
var el = document.createElement('style');
document.head.appendChild(el);
ss = document.styleSheets[0];
document.head.removeChild(el);
}
return function (pseudo_class){
try{
if(!(/^:/).test(pseudo_class)){
pseudo_class = ':'+pseudo_class;
}
ss.insertRule('html'+pseudo_class+'{}',0);
ss.deleteRule(0);
return true;
}catch(e){
return false;
}
};
}();
//test
support_pseudo(':hover'); //true
support_pseudo(':before'); //true
support_pseudo(':hello'); //false
support_pseudo(':world'); //false
If you're OK with using Javascript, you might skip the detection and go right for the shim: Selectivizr

Can I subclass a DOM-class?

I was wondering if I can create a subclass of HTMLDivElement. Like this.
MyDivElement.prototype.pickColor = function()
{
return this.picked;
}
function MyDivElement()
{
this = new HTMLDivElement();
this.picked = 'unknowd';
}
alert(this.picked); // print: 'unkowd'
Is (something like) this possible?
If not, what is the best way to achieve this?
In browsers where __proto__ is exposed and mutable you can sub class DOM elements. It looks like this:
function CustomEl () {
var el = document.createElement('div')
el.__proto__ = CustomEl.prototype
return el
}
CustomEl.prototype.__proto__ = HTMLDivElement.prototype
I also played with it in more detail on jsFiddle. Unfortunately though IE and Opera don't allow __proto__ access and have no plans to in the future as far as I know.
new HTMLDivElement(); throws a TypError "Illegal constructor" in Chrome - so it's not possible.
Update: I've tested in other current browsers, and they throw various types of errors - but they all throw.
Actually, this would work:
function MyDivElement() {
this.picked = 'unknowd';
}
MyDivElement.prototype = document.createElement('div');
var mydiv = new MyDivElement();
But I'm not sure how you could use this pattern...
In some browsers, you can extend the prototype, in others, no. I'll let you guess the ones where you can't. :-) That's not really the same as extending a DOM element, but it does let you do a certain subset of the things for which you might want that facility. The thing is, DOM elements aren't really JavaScript entities; they're only simulacrums provided by the runtime system. (Maybe someday all the jsdom work will actually come to fruition.)
Well ok I'll tell you about the problematic browsers: IE doesn't like that at all. However others do. If you've ever looked at the Prototype library, you'll come across a manifestation of that fact all the time via nasty irritating IE-only bugs when you forget to Prototype-ize a DOM element reference.
(IE9 may be different, but I sort-of doubt it.)
This is the kind of thing that's dirt simple to test over at jsfiddle.
I'm experimenting with this a little bit. A big difficulty is that you need the context of a document to create an element. You can go with window.document as a default, but that's boring.
Here's the POC I'm working on:
function CustomNode(type, parent) {
if (type instanceof Node) {
// Decorate a preexisting node appropriately if called that way.
if (arguments.length === 2 && type.ownerDocument !== parent) {
// Import the node if it's not owned by the requested document.
type = parent.importNode(type, true);
}
return Object.assign(type, this.__proto__);
}
//Normal flow, e.g., new CustomNode("div");
var d = document;
if (parent) {
// Alt document flow, e.g., new CustomNode("div", xmlDoc);
if (parent.nodeType === 9) {
d = parent;
} else {
// Support for new CustomNode("div", parentElement);
// This doesn't append the element, just makes sure
// the documents match
d = parent.ownerDocument;
}
}
var inst;
// Creation flags
if (type[0] === '#') { //text
inst = d.createTextNode(type.substr(1));
} else if (type[0] === '?') { //Processing instruction
type = type.substr(1).split(' ');
inst = d.createProcessingInstruction(type.shift(), type.join(' '));
} else if (type[0] === '[') { // CDATA
inst = d.createCDATASection(type.substr(1));
} else if (type[0] === '/') { // Comment
inst = d.createComment(type.substr(1));
} else { //Everything else gets an element.
inst = d.createElement(type);
}
// DE-COR-ATE
Object.assign(inst, this.__proto__);
return inst;
}
// Decorator for customized NodeLists; probably inefficient. Decorates
// contents with CustomNode
function CustomNodeList(list) {
var Self = this.constructor,
CNode = this.Node;
return Object.assign([].map.call(list, function (node) {
return new CNode(node);
}), this.__proto__);
}
CustomNodeList.prototype = {
// so we know how to wrap...
Node: CustomNode,
text: function () {
return this[0].textContent;
}
};
CustomNode.prototype = {
// So we know how to decorate NodeLists
NodeList: CustomNodeList,
// So we know how to decorate Nodes
Node: CustomNode,
// Easy make-and-attach
new: function (type, attach) {
var CNode = this.Node;
var ret = new CNode(type, this.ownerDocument);
if (attach) {
this.appendChild(ret);
}
return ret;
},
// NodeLists with ES5 iterators!
find: function () {
var CNodeList = this.NodeList;
return new CNodeList(this.querySelectorAll.apply(this, arguments));
},
kids: function () {
var CNodeList = this.NodeList;
return new CNodeList(this.childNodes);
}
};
Mind, this is likely a bad idea: everything in the same scope of these functions (including the elements themselves) will never get garbage collected, as the GC in most browsers is dead-stupid when it comes to elements referencing objects. I'll never use it for production for that reason alone: it's a straight-up memory leak.

Categories

Resources