I am making a javascript script that wants to do something like this:
elementObject.style.transform.replace('...','...');
elementObject.style.webkitTransform.replace('...','...');
And just to clear this up, yes, I have set both of those styles earlier in the script. However, this causes errors because in Chrome, for example, it does not recognize the transform style. Therefore, replace fails because elementObject.style.transform is undefined, and undefined has no method replace. Is there any way to do this without causing these errors? I just want the webkit transform, i do not need moz, o, or any other prefix.
var st = elementObject.style;
if (st.transform !== undefined) st.transform = st.transform.replace('...','...');
// ...
or more general:
function replaceStyle(element, style, text, replacement) {
var vendors = ['webkit', 'moz', 'o', 'ms'];
var st = element.style;
if (st[style] !== undefined) st[style] = st[style].replace(text, replacement);
style = style.charAt(0).toUpperCase()+style.substring(1);
for (var i=0, l=vendors.length; i<l; i++) {
var vendorStyle = vendors[i]+style;
if (st[vendorStyle] !== undefined)
st[vendorStyle] = st[vendorStyle].replace(text, replacement);
}
}
// sample call (setting width from "40px" to "100%")
replaceStyle( myElementObject, 'with', '40px', '100%' );
You must use the Adapter design pattern.
For example:
function transformStyle() {
var tempEl = document.createElement('div'),
elStyle = tempEl.style;
if (typeof elStyle.transform !== 'undefined') {
transformStyle = function (el, val) {
val != null && (el.style.transform = val);
return el.style.transform;
};
} else if (typeof elStyle.webkitTransform !== 'undefined') {
transformStyle = function (el, val) {
val != null && (el.style.webkitTransform = val);
return el.style.webkitTransform;
};
} else {
transformStyle = function () { return ''; }; //ignore when not supported
}
tempEl = null;
elStyle = null;
return transformStyle.apply(this, arguments);
}
var el = document.createElement('div');
transformStyle(el, 'rotate(-2deg)'); //set style
transformStyle(el); //rotate(-2deg)
Obviously you could write something more generic such as a style method that allows accessing or modifying any styles. You could use a list of vendor prefixes to make the code more DRY as well. The previous code was just an example.
Related
I have a javascript/jquery function that displays a notification. I want to make this function display a different mode if they aren't on x, y and z page.
This is how I tried to achieve this:
function display_alert(message, type, delay, mode)
{
type = (typeof type === "undefined") ? "danger" : type;
delay = (typeof delay === "undefined") ? 3000 : delay;
mode = (typeof mode === "undefined") ? 'normal' : mode;
var current_location = document.URL;
var home_locations = ['home', 'remote', 'zip'];
for (var i = 0; i < home_locations.length; i++)
{
if (current_location.toString().indexOf(home_locations[i]) == -1)
{
// alert(home_locations[i]); return;
mode = 'top';
break;
}
}
...
So if document.URL doesn't contain one of the array elements, then I want the mode variable to become top.
I think this is a simple problem, but I just can't see how to fix it.
You could use a regular expression built with your home_locations array :
var home_regex = new RegExp('('+home_locations.join('|')+')');
// home_regex = /(home|remote|zip)/;
if (!home_regex.test(document.URL)) mode = 'top';
You need to reverse the logic in your loop...
var current_location = document.URL;
var home_locations = ['home', 'remote', 'zip'];
var found = false;
for (var i = 0; i < home_locations.length; i++)
{
if (current_location.toString().indexOf(home_locations[i]) >= 0)
{
found = true;
break;
}
}
if (!found) {
mode = 'top';
}
With IE10 not working directly with preserve-3d, I'm failing miserably at adding a test to fall back on this.
I've seen there was a pull request related to this recently integrated into Modernizr, however .preserve3d also seems to do nothing (built from repo). https://github.com/Modernizr/Modernizr/commit/4c8f8905e0f6487c85c91f4bd67d51b62b40b993
Checking for specifically IE10 seems very bad. Can I avoid this?
Applying MS idea of moving the transform to the children causes interesting results. I assume that it is not an answer for what I'm trying to do?
This should work on IE8,9,11(?),Chrome,Firefox,Safari
What I hope to do...
Modernizr.addTest('csstransformspreserve3d', function () {
var prop = Modernizr.prefixed('transformStyle');
var val = 'preserve-3d';
var computedStyle;
if(!prop) return false;
prop = prop.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-');
Modernizr.testStyles('#modernizr{' + prop + ':' + val + ';}', function (el, rule) {
computedStyle = win.getComputedStyle ? getComputedStyle(el, null).getPropertyValue(prop) : '';
});
return (computedStyle === val);
});
http://jsfiddle.net/LwaMY/1/
I wrote more general solution for case when you need to check for css value support.
Example of use:Modernizr.addValueTest('transform-style','preserve-3d');
Demo on codepen.
Implementation:
Modernizr.addValueTest = function(property,value){
Modernizr.addTest(property+value , function () {
var element = document.createElement('link');
var body = document.getElementsByTagName('HEAD')[0];
var properties = [];
var upcaseProp = property.charAt(0).toUpperCase() + property.slice(1);
properties[property] =property;
properties['Webkit'+upcaseProp] ='-webkit-'+property;
properties['Moz'+upcaseProp] ='-moz-'+property;
properties['ms'+upcaseProp] ='-ms-'+property;
body.insertBefore(element, null);
for (var i in properties) {
if (element.style[i] !== undefined) {
element.style[i] = value;
}
}
//ie7,ie8 doesnt support getComputedStyle
//so this is the implementation
if(!window.getComputedStyle) {
window.getComputedStyle = function(el, pseudo) {
this.el = el;
this.getPropertyValue = function(prop) {
var re = /(\-([a-z]){1})/g;
if (prop == 'float') prop = 'styleFloat';
if (re.test(prop)) {
prop = prop.replace(re, function () {
return arguments[2].toUpperCase();
});
}
return el.currentStyle[prop] ? el.currentStyle[prop] : null;
};
return this;
};
}
var st = window.getComputedStyle(element, null),
currentValue = st.getPropertyValue("-webkit-"+property) ||
st.getPropertyValue("-moz-"+property) ||
st.getPropertyValue("-ms-"+property) ||
st.getPropertyValue(property);
if(currentValue!== value){
element.parentNode.removeChild(element);
return false;
}
element.parentNode.removeChild(element);
return true;
});
}
Say I've selected a span tag in a large html document. If I treat the entire html document as a big nested array, I can find the position of the span tag through array indexes. How can I output the index path to that span tag? eg: 1,2,0,12,7 using JavaScript.
Also, how can I select the span tag by going through the index path?
This will work. It returns the path as an array instead of a string.
Updated per your request.
You can check it out here: http://jsbin.com/isata5/edit (hit preview)
// get a node's index.
function getIndex (node) {
var parent=node.parentElement||node.parentNode, i=-1, child;
while (parent && (child=parent.childNodes[++i])) if (child==node) return i;
return -1;
}
// get a node's path.
function getPath (node) {
var parent, path=[], index=getIndex(node);
(parent=node.parentElement||node.parentNode) && (path=getPath(parent));
index > -1 && path.push(index);
return path;
}
// get a node from a path.
function getNode (path) {
var node=document.documentElement, i=0, index;
while ((index=path[++i]) > -1) node=node.childNodes[index];
return node;
}
This example should work on this page in your console.
var testNode=document.getElementById('comment-4007919');
console.log("testNode: " + testNode.innerHTML);
var testPath=getPath(testNode);
console.log("testPath: " + testPath);
var testFind=getNode(testPath);
console.log("testFind: " + testFind.innerHTML);
Using jquery:
var tag = $('#myspan_id');
var index_path = [];
while(tag) {
index_path.push(tag.index());
tag = tag.parent();
}
index_path = index_path.reverse();
Using the DOM
node = /*Your span element*/;
var currentNode = node;
var branch = [];
var cn; /*Stores a Nodelist of the current parent node's children*/
var i;
while (currentNode.parentNode !== null)
{
cn = currentNode.parentNode.childNodes;
for (i=0;i<cn.length;i++)
{
if (cn[i] === currentNode)
{
branch.push(i);
break;
}
}
currentNode = currentNode.parentNode;
}
cn = document.childNodes;
for (i=0;i<cn.length;i++)
{
if (cn[i] === currentNode)
{
branch.push(i);
break;
}
}
node.innerHTML = branch.reverse().join(",");
composedPath for native event.
(function (E, d, w) {
if (!E.composedPath) {
E.composedPath = function () {
if (this.path) {
return this.path;
}
var target = this.target;
this.path = [];
while (target.parentNode !== null) {
this.path.push(target);
target = target.parentNode;
}
this.path.push(d, w);
return this.path;
};
}
})(Event.prototype, document, window);
use:
var path = event.composedPath()
I'm dealing with a ball-of-mudd project that uses frames & iframes to create a "customizable" interface (like people did in 2002).
The application runs from within a hta and kind of emulates a real WPF style app. I need to capture keys so I can selectively change/refresh some of the subframes.
What I'm trying to do is, if there was a sub-sub frame called desktop and that had some frames in it how would I capture an event, safely, across all frames; and refresh a frames subframes?
Any help appreciated; I accept no responsibility for nausia caused by repeating the last paragraph too many times. :)
Answering to get the formatting
arrFrames[i].document.onkeypress = function(){
var evtobj = window.event ? event : e;
evtobj.cancelBubble = true;
if (evtobj.stopPropagation){ evtobj.stopPropagation();}
top.console.log(evtobj.type+' - '+(evtobj.which?evtobj.which:evtobj.keyCode));
};
I don't know anything about HTA, but the question is marked as javascript / jquery / iframe, so i'll guess it isn't a problem...
You can use an object in window.top to manage your events in a centralized place.
In your main window, you use something like:
var getTopObject = function() {
return window.top.topObject;
}
var TopClass = function () {
this.processClick = function (frame) {
//do something...
alert('click in ' + frame.document.location.toString());
var msj = frame.document.getElementById("msj");
msj.innerHTML = "change!";
};
}
window.top.topObject = new TopClass();
And then, on every iframe, you put:
window.onclick = function () { getTopObject().processClick(window); };
That way you get notified of the click event.
Also note that inside the 'processClick' function in the example you can access the iframe document.
Of course, you can do this a lot more complex, but that's the basic idea. You will have to deal with different events in your case.
Hope this helps, and sorry for my english!
Working; digs through the frames in a loop using a function calling itself; I limited it to 8 rather as I know thats the deepest it will get. You can always change that yourself.
var XXX_util_keyboard = function()
{
//"private" variables:
var objTopWindow = top.window.frames,
arrFrames = [],
MaxDepth = 8;
//"private" methods:
var AddToArray = function(obj){
if(typeof obj.document != "undefined") {
arrFrames.push(obj);
return true;
}
return false;
};
var DeleteFromArray = function(obj){
if(typeof obj != "undefined") {
arrFrames.splice(arrFrames.indexOf(obj), 1);
return true;
}
return false;
};
var FrameLoop = function(objFrames){
if(MaxDepth > 0){
if(objFrames !== null)
{
for(var k = 0; k < objFrames.frames.length; k++)
{
var tmp = objFrames.frames[k];
AddToArray( tmp );
FrameLoop( tmp );
}
this.MaxDepth--;
}
}
};
var AttachEvent = function(key, fn) {
for(var i = 0; i < arrFrames.length; i++){
arrFrames[i].document.onkeypress = function(e) {
var evt = e || window.event,
charCode;
if(evt === null){ evt = this.parentWindow.event; /*IE doesnt capture scope correctly*/ }
charCode = evt.keyCode || evt.which;
alert(charCode);
evt.cancelBubble = true;
if (evt.stopPropagation){ evt.stopPropagation();}
};
}
};
return {
init: function()
{
AddToArray(this.getTopWindow()[0]);
FrameLoop(this.getTopWindow()[0]);
},
getFrames: function()
{
if(arrFrames.length < 1){ FrameLoop(objTopWindow[0]); }
return arrFrames;
},
getTopWindow: function()
{
return objTopWindow === undefined ? window.frames : objTopWindow.window.frames;
},
attachEvent: function()
{
if(arrFrames.length < 1){ FrameLoop(objTopWindow[0]); }
AttachEvent();
}
};
}();
Say I've selected a span tag in a large html document. If I treat the entire html document as a big nested array, I can find the position of the span tag through array indexes. How can I output the index path to that span tag? eg: 1,2,0,12,7 using JavaScript.
Also, how can I select the span tag by going through the index path?
This will work. It returns the path as an array instead of a string.
Updated per your request.
You can check it out here: http://jsbin.com/isata5/edit (hit preview)
// get a node's index.
function getIndex (node) {
var parent=node.parentElement||node.parentNode, i=-1, child;
while (parent && (child=parent.childNodes[++i])) if (child==node) return i;
return -1;
}
// get a node's path.
function getPath (node) {
var parent, path=[], index=getIndex(node);
(parent=node.parentElement||node.parentNode) && (path=getPath(parent));
index > -1 && path.push(index);
return path;
}
// get a node from a path.
function getNode (path) {
var node=document.documentElement, i=0, index;
while ((index=path[++i]) > -1) node=node.childNodes[index];
return node;
}
This example should work on this page in your console.
var testNode=document.getElementById('comment-4007919');
console.log("testNode: " + testNode.innerHTML);
var testPath=getPath(testNode);
console.log("testPath: " + testPath);
var testFind=getNode(testPath);
console.log("testFind: " + testFind.innerHTML);
Using jquery:
var tag = $('#myspan_id');
var index_path = [];
while(tag) {
index_path.push(tag.index());
tag = tag.parent();
}
index_path = index_path.reverse();
Using the DOM
node = /*Your span element*/;
var currentNode = node;
var branch = [];
var cn; /*Stores a Nodelist of the current parent node's children*/
var i;
while (currentNode.parentNode !== null)
{
cn = currentNode.parentNode.childNodes;
for (i=0;i<cn.length;i++)
{
if (cn[i] === currentNode)
{
branch.push(i);
break;
}
}
currentNode = currentNode.parentNode;
}
cn = document.childNodes;
for (i=0;i<cn.length;i++)
{
if (cn[i] === currentNode)
{
branch.push(i);
break;
}
}
node.innerHTML = branch.reverse().join(",");
composedPath for native event.
(function (E, d, w) {
if (!E.composedPath) {
E.composedPath = function () {
if (this.path) {
return this.path;
}
var target = this.target;
this.path = [];
while (target.parentNode !== null) {
this.path.push(target);
target = target.parentNode;
}
this.path.push(d, w);
return this.path;
};
}
})(Event.prototype, document, window);
use:
var path = event.composedPath()