I am trying to create a div element which shows some text as popup. But the problem is when i create the element using javascript, the div already contains the default style which is defined in document style sheet or external css. Like for example:
popup = document.createElement("div");
popup.setAttribute("id","myElement");
popup.style.left = "100px";
popup.style.top = "100px";
popup.style.width = "100px";
popup.style.height = "100px";
document.body.appendChild(popup);
and now when it shows up, it already has colors, borders etc because in css there is this div style which is being applied on this element. I want it to not inherit anything which is defined for the document itself, how can i do that, or may be overwrite the original style??
A way to achieve this is to override all of the available properties set with the css in this document. But with JavaScript is a lot of work.
Better way is to add an id or class to the newly created element. The style should have all of the properties that are applicable for the type of element you are creating. If you miss a property it will be set from the present css.
I would give these new divs a specific class name and link a custom stylesheet that resets all properties for that class to the desired defaults (ensuring that the linked styles have the necessary precedence in the cascade). Moving this reset into your JavaScript sounds like a bad idea.
So I made something like this. It may not be ideal solution but it atleast does what i wanted:
function displayMenu(){
var popup;
//see if there is already default style defined in the document
var styleSheets = document.styleSheets;
var size = styleSheets.length;
var cssRules = new Array();
var rules = "";
var css, len, st, sp;
for (i=0;i<size;i++){
cssRules[i] = styleSheets[i].rules || styleSheets[i].cssRules;
for (j=0;j<cssRules[i].length;j++){
if(cssRules[i][j].cssText.search(/div/i) != -1){
css = cssRules[i][j].cssText;
css = css.substr(((css.search("{"))+1),((css.search("}"))-(((css.search("{"))+1))));
if((css.search("{") == -1) && (css.search("}") == -1)) {
//no of css-properties in this specific rule
len = css.split(";").length - 1;
for (k=0;k<len;k++){
st = css.search(";") + 1;
rules += css.substr(0,(css.substr(0,st).search(":")+1)) + "0\n";
css = css.substr(st);
}
} else {} //ignore this rule
}
}
}
var reset = '.myStyle { '+ rules +' }\n';
//now create a css Class which overwrite default document properties for this <div> element
var myStyle = document.createElement('style');
myStyle.type = 'text/css';
//TODO: should be replaced with style from arguments
myStyle.innerHTML = reset;
document.getElementsByTagName('head')[0].appendChild(myStyle);
//start creating the popup menu:
var popup;
popup = document.createElement("div");
popup.setAttribute("id","guide_popup");
popup.setAttribute("class","myStyle");
//now define own style rules: (All basic properties need to be defined as there is none by defualt now)
popup.style.top = top;
popup.style.left = left;
popup.style.width = width;
popup.style.height = height;
popup.style.zIndex = index;
//TODO: should be replaced with str in aruguments
var popup_text = document.createTextNode("This is my sample text");
popup.appendChild(popup_text);
//finally process the DOM
document.body.appendChild(popup);
}
Related
I'm sure the answer is no, but is it possible to determine the width of an element before it is appended to the DOM?
Once it's appended, I know I can use offsetWidth and offsetHeight.
Thanks
The trick is to show the element (display:block) but also hide it (visibility:hidden) and to set it’s position to absolute so that it doesn’t affect the page flow.
The MooTools Element.Measure class does this, as Oscar mentioned.
The Mootools Element.Measure functionality that Oscar mentioned is awesome. For those that use jQuery, here's a quick plugin that accomplishes the same thing:
$.fn.measure = (fn)->
el = $(this).clone(false)
el.css
visibility: 'hidden'
position: 'absolute'
el.appendTo('body')
result = fn.apply(el)
el.remove()
return result
You can call it like this, making sure to return the value (thanks Sam Fen for pointing that out!):
width = $('.my-class-name').measure( function(){ return this.width() } )
Modified the code a bit. Here is a pure JS solution:
function measure(el, fn) {
var pV = el.style.visibility,
pP = el.style.position;
el.style.visibility = 'hidden';
el.style.position = 'absolute';
document.body.appendChild(el);
var result = fn(el);
el.parentNode.removeChild(el);
el.style.visibility = pV;
el.style.position = pP;
return result;
}
var div = document.createElement('div');
div.innerHTML = "<p>Hello</p><br/>";
alert(div.offsetHeight); // 0
alert(measure(div, function(el){return el.offsetHeight})); // 68
What you can do with MooTools is use the Element.Measure class - meaning, you inject the element to the DOM, but keep it hidden. Now, you can measure the element without actually showing it.
http://mootools.net/docs/more/Element/Element.Measure
It is not possible, at least not accurately, because styling affects these properties, and where it's put determines how it is styled and what rules affect it.
For example placing a <p></p> in the page would by default be the width of the body if appended as a child to it, but if you appeneded it inside for example a <div style="width: 100px;"></div>, then you see how that quickly changes things.
/**
* Get bounding client rect for an element (not exists at current DOM tree)
* #param {!HTMLElement} el
* #return {!Promise<!ClientRect>}
*/
function getElementRect(el) {
return new Promise(resolve => {
const element = el.cloneNode(true);
element.style.visibility = "hidden";
element.style.position = "absolute";
document.body.appendChild(element);
resolve(element.getBoundingClientRect());
element.remove();
});
}
const div = /** #type {!HTMLElement} */ (document.createElement("div"));
div.innerHTML = "<p>Hello</p><br/>";
// Execute
(async () => {
const rect = await getElementRect(div);
console.log(rect.width);
})();
DEMO
A slightly different version of #kashesanders: Add the element to a div and put that div inside the DOM.
function getSizeOfNonDomElement(domElement)
{
// setup
let div = document.createElement("div");
div.style.position = "absolute";
div.style.visibility = "hidden";
div.style.display = "block";
div.appendChild (domElement);
document.body.appendChild (div);
// run
let rect = domElement.getBoundingClientRect ();
// cleanup
document.body.removeChild (div);
div.removeChild (domElement);
return rect;
}
If you want to make it more secure, add a construct to check whether it already has a parent and/or is inside the DOM.
I want to create a JavaScript object for an element which describes all the computed styles that have an effect, either directly or from a parent. Ideally in the same fashion as inspect element does (the list you see under the elements tab of the inspect console in chrome or Firefox).
Like this.
the section of inspect element I need to try to recreate;
I want to get the styles that effect any particular element selected and then know the class name or ID name where they are being set, just like in the chrome inspect element.
So far I can get the classes and ID's of that element, then I can get the style rules for each of those. But, I need to also get the styles of the parents which are in effect through parental hierarchy. First I thought to traverse up the DOM getting all the parent styles along the way and then filtering out any which don't effect children. But I know this will be fairly complicated. Can anyone help add to what I have so far?
I still need to figure out how to get the #queries that apply to the element, and that also needs to be responsive. I.e. if the screen resizes and a new media query is selected by the CSS, it needs to re calculate to get the corresponding queries. Also I need to filter out a list of rules that are not applicable to children. Is there such a list anywhere?
I thought id try another way. I tried to get the computed styles like this..
var cssClassObj = [];
cssClassObj['classA'] = [];
cssClassObj['classA']['background'] = '#000';
cssClassObj['classB'] = [];
cssClassObj['classB']['color'] = '#fff';
cssClassObj['classC'] = [];
cssClassObj['classC']['color'] = 'red';
var cssIDObj = [];
cssIDObj['starthere'] = [];
cssIDObj['starthere']['text-decoration'] = 'underline';
var i = 0;
var StyleOBJ = [];
var el = document.getElementById('starthere');
//var style = getComputedStyle(link);
var overwrittenRules = [];
var thisParentTree = [];
var ThisStyleOBJ = getObj(StyleOBJ, el, overwrittenRules, thisParentTree);
console.log(ThisStyleOBJ);
function getObj(styleObj, el, overwrittenRules, parentTree, start = true) {
if (el != document) {
var classes = el.classList;
for (var className in classes) {
if (classes.hasOwnProperty(className)) {
var cssStyles = cssClassObj[classes[className]] || null;
for (var style in cssStyles) {
if (cssStyles.hasOwnProperty(style)) {
if (typeof overwrittenRules[style] !== "undefined") {
cssStyles[style] += " :Omitted!!";
} else {
overwrittenRules[style] = true
}
}
}
StyleOBJ['class-' + classes[className]] = cssClassObj[classes[className]] || null;
if (!start) {
parentTree[++i] = el;
StyleOBJ['class-' + classes[className]]['Parentree'] = parentTree.slice();
}
}
}
if (el.id) {
StyleOBJ['id-' + el.id] = cssIDObj[el.id] || null;
}
var parent = el.parentNode;
styleObj = getObj(styleObj, parent, overwrittenRules, parentTree, false);
}
return styleObj;
}
.classA {
background: #000;
}
.classB {
color: #fff;
}
.classC {
color: red;
}
<body>
<div class="classA">
<div class="classB">
<p id="starthere" class="classC">
Open up the console to see the object. It contains all the styles and anything which is overwritten has been marked as Omitted. Each element has a parent tree of nodes, if it is a parent.
<br/><br/>
Still to do is;
<br/>
Check if !important is used and update the omitted children.
<br/>
#queries which are currently effecting the element
<br/>
find a way to automatically turn the style sheet into an object instead of having to hardcode it
</p>
</div>
</div>
Please Note that for some reason SO doesn't show the resulting object in the snippet so you have to open up the console to see the output when you click run.
It's coming together now, but needs to be finished.
Thanks in advance.
The code below makes a Div for every object it finds in an array. It then adds a header, a paragraph, an image, and an anchor.
The div's actually look like rectangles on top of one another. What I would like to do is add an onclick attribute to the divs based on the keys within my objects ( coffeShops[i].menu). When I do so though, clicking just doesn't do anything and i get this error message in console
"Uncaught TypeError: Cannot read property 'menu' of undefined"
This only happens when I try window.open with the object key coffeShops[i].menu. If I swap that out with "http://www.google.com" it works just fine. Also, the links from that variable show up in the console just fine. So I know it's getting the data from the object just fine. But for some reason it doesn't want to open from the div.
FYI I'm extremely new to this so apologies if the explanation doesn't make sense.
var containerDiv = document.getElementById("container");
console.log(containerDiv);
for (var i = 0; i < coffeeShops.length; i++){
var launchMenu = function(){
window.open(coffeeShops[i].menu);
}
console.log(coffeeShops[i].menu);
var coffeeShopDiv = document.createElement("div");
coffeeShopDiv.className = "coffeeShop";
coffeeShopDiv.onclick = launchMenu;
containerDiv.appendChild(coffeeShopDiv);
var coffeeShopImage = document.createElement("img");
coffeeShopImage.src = coffeeShops[i].image;
coffeeShopImage.className = "coffeeImage";
coffeeShopDiv.appendChild(coffeeShopImage);
var coffeeShopHeader = document.createElement("h1");
coffeeShopHeader.className = "coffeeHeader"
coffeeShopHeader.innerHTML = coffeeShops[i].name;
coffeeShopDiv.appendChild(coffeeShopHeader);
var coffeeShopPar = document.createElement("p");
coffeeShopPar.className = "coffeeDescription"
coffeeShopPar.innerHTML = coffeeShops[i].description;
coffeeShopDiv.appendChild(coffeeShopPar);
var coffeeMenu = document.createElement("a");
coffeeMenu.href = coffeeShops[i].menu;
coffeeMenu.innerHTML = "MENU"
coffeeShopDiv.appendChild(coffeeMenu);
};
EDITED: in light of comment
It looks like you need to separate the function which opens the new window from the code which generates page items. By slightly restructuring your code this can be achieved by using the element.addEventListener() method.
/* place the 'launchMenu' outside of
the for-loop so it can be accessed
by each of the target elements */
function launchMenu(event){
/* get the class attribte of
the clicked element */
var clickedClass = event.target.className;
var targetElement,
link;
/* is the clicked element the containing div? */
if (clickClass !== 'coffeeShop') {
/* nope */
targetElement = event.target.parentNode;
} else {
/* yep */
targetElement = event.target;
}
/* get 'data-url' of target */
link = targetElement.getAttribute('data-url');
/* open the new window */
window.open(link);
}
/* elsewhere: create elements as before */
for (var i = 0; i < coffeeShops.length; i++){
var coffeeShopDiv = document.createElement("div");
coffeeShopDiv.className = "coffeeShop";
/* add 'data-' attribute to target div */
coffeeShopDiv.setAttribute('data-url', coffeeShops[i].menu);
containerDiv.appendChild(coffeeShopDiv);
var coffeeShopImage = document.createElement("img");
coffeeShopImage.src = coffeeShops[i].image;
coffeeShopImage.className = "coffeeImage";
coffeeShopDiv.appendChild(coffeeShopImage);
var coffeeShopHeader = document.createElement("h1");
coffeeShopHeader.className = "coffeeHeader"
coffeeShopHeader.innerHTML = coffeeShops[i].name;
coffeeShopDiv.appendChild(coffeeShopHeader);
var coffeeShopPar = document.createElement("p");
coffeeShopPar.className = "coffeeDescription"
coffeeShopPar.innerHTML = coffeeShops[i].description;
coffeeShopDiv.appendChild(coffeeShopPar);
var coffeeMenu = document.createElement("a");
coffeeMenu.className = 'coffeeMenu';
coffeeMenu.innerHTML = "MENU"
coffeeShopDiv.appendChild(coffeeMenu);
/* attach an eventListener to each
created '.coffeeShopDiv' element */
coffeeMenu.addEventListener(
'click', launchMenu, false
);
};
The target element (coffeeShopDiv) now contains a url-reference in its 'data-url' attriubte and has an event listener assigned.
When the target element, or any of her child elements, are clicked the launchMenu() function will be invoked. This function checks which element was clicked by comparing the className attribute to the class name of 'coffeeShopDiv' - such that if the class name of the clicked element is not 'coffeeShop' the targetElement variable is assigned to the clicked elements parent Node (that is, 'coffeShopDiv').
The 'data-url' reference is obtained and used in window.open().
Also see:
Using data-* attributes
Event.target
Quriksmode: Introduction to Events
This StackOverflow answer about using
window.open()
I can't get this to work:
<p>
First
Second
Third
Fourth
</p>
<script>
var link = document.getElementsByTagName("a");
link.style.fontSize = '16px';
link.style.textDecoration = 'none';
link.style.color = '#333333';
link.style.fontWeight = 'bold';
</script>
I'm trying to add CSS styles (font-size, text-decoration, color, font-weight) to all the <a> tags of my HTML code.
This isn't working because you're trying to apply the changes to the list vs. the individual links. You need to loop through the links and apply the changes to the individual items
var all = document.getElementsByTagName("a");
for (var i = 0; i < all.length; i++) {
var link = all[i];
link.style.fontSize = '16px';
link.style.textDecoration = 'none';
link.style.color = '#333333';
link.style.fontWeight = 'bold'
}
Additionally it looks like your script is running before the a elements are defined. Hence the getElementsByTagName will return an empty collection. Try moving the script to after the definition of the anchor elements
When the closing tag of that <script> block is encountered, the whole code in it is evaluated. Since anything after </script> has not been parsed yet, the result from document.getElementsByTagName('a') is not as expected.
Wrap the method in an onload or DOMContentLoaded event.
It seems that you want to target all anchor elements. Instead of looping through all anchorts, you'd better append a <style> element with the given CSS text:
window.addEventListener('load', function() {
// `window.attachEvent('onload', function() {` for old IE versions
var style = document.createElement('style');
var cssText = 'a,a:link,a:visited {' +
'font-size:16px;text-decoration:none;color:#333;font-weight:bold;}';
style.appendChild(document.createTextNode(cssText));
document.getElementsByTagName('head')[0].appendChild(style);
}, false);
I'm sure the answer is no, but is it possible to determine the width of an element before it is appended to the DOM?
Once it's appended, I know I can use offsetWidth and offsetHeight.
Thanks
The trick is to show the element (display:block) but also hide it (visibility:hidden) and to set it’s position to absolute so that it doesn’t affect the page flow.
The MooTools Element.Measure class does this, as Oscar mentioned.
The Mootools Element.Measure functionality that Oscar mentioned is awesome. For those that use jQuery, here's a quick plugin that accomplishes the same thing:
$.fn.measure = (fn)->
el = $(this).clone(false)
el.css
visibility: 'hidden'
position: 'absolute'
el.appendTo('body')
result = fn.apply(el)
el.remove()
return result
You can call it like this, making sure to return the value (thanks Sam Fen for pointing that out!):
width = $('.my-class-name').measure( function(){ return this.width() } )
Modified the code a bit. Here is a pure JS solution:
function measure(el, fn) {
var pV = el.style.visibility,
pP = el.style.position;
el.style.visibility = 'hidden';
el.style.position = 'absolute';
document.body.appendChild(el);
var result = fn(el);
el.parentNode.removeChild(el);
el.style.visibility = pV;
el.style.position = pP;
return result;
}
var div = document.createElement('div');
div.innerHTML = "<p>Hello</p><br/>";
alert(div.offsetHeight); // 0
alert(measure(div, function(el){return el.offsetHeight})); // 68
What you can do with MooTools is use the Element.Measure class - meaning, you inject the element to the DOM, but keep it hidden. Now, you can measure the element without actually showing it.
http://mootools.net/docs/more/Element/Element.Measure
It is not possible, at least not accurately, because styling affects these properties, and where it's put determines how it is styled and what rules affect it.
For example placing a <p></p> in the page would by default be the width of the body if appended as a child to it, but if you appeneded it inside for example a <div style="width: 100px;"></div>, then you see how that quickly changes things.
/**
* Get bounding client rect for an element (not exists at current DOM tree)
* #param {!HTMLElement} el
* #return {!Promise<!ClientRect>}
*/
function getElementRect(el) {
return new Promise(resolve => {
const element = el.cloneNode(true);
element.style.visibility = "hidden";
element.style.position = "absolute";
document.body.appendChild(element);
resolve(element.getBoundingClientRect());
element.remove();
});
}
const div = /** #type {!HTMLElement} */ (document.createElement("div"));
div.innerHTML = "<p>Hello</p><br/>";
// Execute
(async () => {
const rect = await getElementRect(div);
console.log(rect.width);
})();
DEMO
A slightly different version of #kashesanders: Add the element to a div and put that div inside the DOM.
function getSizeOfNonDomElement(domElement)
{
// setup
let div = document.createElement("div");
div.style.position = "absolute";
div.style.visibility = "hidden";
div.style.display = "block";
div.appendChild (domElement);
document.body.appendChild (div);
// run
let rect = domElement.getBoundingClientRect ();
// cleanup
document.body.removeChild (div);
div.removeChild (domElement);
return rect;
}
If you want to make it more secure, add a construct to check whether it already has a parent and/or is inside the DOM.