I can't seem to define a for loop function, what am I doing wrong?
My HTML code:
<body onload="generate()">
My Javascript code:
function generate(){
for(i = 0; i < 150; i++) {
document.write("<div></div>");
}
};
Your loop is fine (other than that you don't declare i, and so you fall prey to the Horror of Implicit Globals), it's document.write that's the problem. You can only use document.write in an inline script, not after the page has been loaded (e.g., not in the body load event). If you use document.write after the page is loaded, it tears down the page and replaces it with what you output (because there's an implicit document.open call). So in your case, your page disappears and 150 blank divs are there instead.
To manipulate the page after load, you'll want to use the DOM, references:
DOM2 Core - Widely supported by browsers
DOM HTML bindings - Widely suppored by browsers
DOM3 Core - Fairly well supported, some gaps
For instance, here's how you'd write your generate function to append 150 blank divs to the page:
function generate() {
var i;
for (i = 0; i < 150; i++){
document.body.appendChild(document.createElement('div'));
}
}
Or more usefully, 150 divs with their numbers in:
function generate() {
var i, div;
for (i = 0; i < 150; i++){
div = document.createElement('div');
div.innerHTML = "I'm div #" + i;
document.body.appendChild(div);
}
}
Live copy
Separately, if you're going to do any significant DOM manipulation, it's well worth using a good JavaScript browser library like jQuery, Prototype, YUI, Closure, or any of several others. These smooth over browser differences (and outright bugs), provide useful utility functions, and generally let you concentrate on what you're actually trying to do rather than fiddling about with inconsistencies between IE and Chrome, Opera and Safari...
The document.write mentioned in https://stackoverflow.com/q/8257414/295783 is the reason it does not work. The first document.write wipes the page including the script that is executing.
A better way is
<html>
<head>
<script type="text/javascript">
var max = 150;
window.onload=function() {
var div, container = document.createElement('div');
for (var i=0;i<max;i++) {
div = document.createElement('div');
container.appendChild(div);
}
document.body.appendChind(container);
}
</script>
</head>
<body>
</body>
</html>
window.addEventListener("DOMContentLoaded", function() {
for(var i = 0; i < 150; i++) {
document.body.appendChild( document.createElement("div") );
}
}, false);
This should work, didn't test it though.
It's important to wait for the 'DOMContenLoaded' event, because else some elements might not exist at the time your script was executed.
do the folowing;
function generate(){
var echo="";
for(i = 0; i < 150; i++){
echo+="<div></div>";
}
document.getElementById("someID").innerHTML=echo;
//document.write(echo); //only do this, if you want to replace the ENTIRE DOM structure
};
Related
I'm admittedly a bit new to this; I know HTML/CSS more then I know Javascript. I know enough to code an extension, but I'm having problems that I believe may be specific to Chrome. I don't want to be too specific, but as part of my extension I'd like to remove styles from all webpages.
Here's what I have:
manifest.json
{
"manifest_version": 2,
"name": "[redacted]",
"description": "[redacted]",
"version": "1.0",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["script.js"],
"run_at": "document_start"
}
]
}
script.js
function lolxd() {
var hs = document.getElementsByTagName('style');
for (var i=0, max = hs.length; i < max; i++) {
hs[i].parentNode.removeChild(hs[i]);
}
window.onload = lolxd();
I've tried multiple different scripts, none of which have worked. Strangely the extension loads fine, but the Javascript doesn't work and I can still see styles on all webpages. Any help?
You can recursively iterate through all elements and remove the style attribute, remove all linked (external) stylesheets and embedded stylesheets.
BEFORE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://www.w3schools.com/html/styles.css">
<style>
p {
font-size: 150%;
}
</style>
</head>
<body>
<h1>This is a heading</h1>
<p style="font-weight: bold;">This is a paragraph.</p>
</body>
</html>
AFTER
function removeStyles(el) {
// code to remove inline styles
el.removeAttribute('style');
if (el.childNodes.length > 0) {
for (var child in el.childNodes) {
/* filter element nodes only */
if (el.childNodes[child].nodeType == 1)
removeStyles(el.childNodes[child]);
}
}
// code to remove embedded style sheets
var styletag = document.getElementsByTagName('style');
var i = 0;
for (; i < styletag.length; i++) {
styletag[i].remove();
}
// code to remove external stylesheets
var stylesheets = document.getElementsByTagName('link'),
sheet;
for (i = 0; i < stylesheets.length; i++) {
sheet = stylesheets[i];
if (sheet.getAttribute('rel').toLowerCase() == 'stylesheet')
sheet.parentNode.removeChild(sheet);
}
}
removeStyles(document.body);
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://www.w3schools.com/html/styles.css">
<style>
p {
font-size: 150%;
}
</style>
</head>
<body>
<h1>This is a heading</h1>
<p style="font-weight: bold;">This is a paragraph.</p>
</body>
</html>
A simple answer that removes most styles is pretty basic.
document.body.parentElement.innerHTML = document.body.innerHTML;
This works by kinda placing all HTML inside the head element, so I'll give props to any screen reader that can do any decent job on this new webpage. However, there are two main problems with this method.
As mentioned before, accessibility will be ruined.
The style attribute will still work.
So, To solve both these problems, we might me able to do something like this:
document.querySelectorAll("*").forEach(elem => { //this gets ALL the elements on the page.
elem.removeAttribute('style') // fix problem 2
if (elem.nodeName.toLowerCase() === "style") { // if element is a style element, remove it.
elem.remove()
return;
};
if (elem.nodeName.toLowerCase() === "link" && elem.hasAttribute("href") && elem.hasAttribute("rel")) { // Check if the element is a link, has href and rel attributes
if (elem.getAttribute('rel') === "stylesheet") {
elem.remove();
return;
};
};
});
This, for most practical applications is pretty good, of course, if there were children in the style and/or link elements, it would also remove them, so there may be a way to retain those, and a few other minor things. If your target site is large, and thus has many elements, this could drastically add to the loading time, because it has to loop over every element, and do some (admittedly quite quick) operations to them So, I devised a new solution using the Intersection Observer API. This only executes on elements that are visible to the user, so it should increase performance.
const observer = new IntersectionObserver((entries)=>{
entries.forEach(entry => {
entry.target.removeAttribute('style')
});
}, {
rootMargin:"1%",
threshold:0.01
});
document.querySelectorAll("*").forEach(elem=>{
if (elem.nodeName.toLowerCase() !== "style" && elem.nodeName.toLowerCase !== "link") {
observer.observe(elem);
} else {
if (elem.nodeName.toLowerCase() === "style") {
elem.remove();
return;
};
if (elem.nodeName.toLowerCase() === "link" && elem.hasAttribute("href") && elem.hasAttribute("rel")) {
if (elem.getAttribute("rel") === "stylesheet") {
elem.remove();
return;
};
};
};
});
Note: We do not need the root to be supplied, because by default, it is the browser viewport, which is what we want. We only call this function when 1% of the element is visible. We still need to have the style and link elements checked, because they are never shown, so this is not a major performance improvement, but it is there, you do need quite a bit of elements for it to pay off though, otherwise it would be slower.
One interesting thing I noticed, when I tried it on Google, was that the Search mangifer icon is huge, as well as the X, also you can see all of the 'I'm feeling lucky' options. When you hover over your icon, which I took 5 minutes to find, was that it flashes the tooltip, so possibly google is creating elements here, but most likely is that they are setting "style=display:block;" but then the code realizes that it is visible, removing the style attribute. Also tried it on StackOverflow and it does some crazy stuff, absolutely nothing, but the slow version works? I have no idea why, but that's interesting. This could be used for nefarious means, because you could use it to see data that you weren't supposed to, though I think that DevTools and the View Source feature do that better. However with this you don't have to deal with hundreds of lines of HTML,CSS, and JavaScript, its just right there. This does not interfere with the functionality of the page, just makes most look horrible, but I guess that sometimes it could make some look better. Please someone fix the version that uses the Intersection Observer API, because on some sites it works fine, but others, like StackOverflow, not so much.
I also think that this is the best approach because, while all the others (as of current) don't take into account that the link element is not entirely for stylesheets, but that is of course the main use of it. It can be used to render icons, including the favicon. The docs of the link element is here. So, if you did want to remove some icons and possibly the favicon, then just remove the filter for rel=stylesheet and if it has an href.
like this:
function removeCSS() {
// remove `link`
var links = document.getElementsByTagName('link');
for(var i = 0, len = links.length; i < len; i++) {
links[i].parentNode.removeChild(links[i]);
}
// remove `style`
var styles = document.getElementsByTagName('style');
for(var j = 0, len = styles.length; j < len; j++) {
styles[j].parentNode.removeChild(styles[j]);
}
// remove inline style
var nodes = document.querySelectorAll('[style]');
for(var h = 0, len = nodes.length; h < len; h++) {
nodes[h].removeAttribute('style');
}
}
$myWindow.on('resize', function(){
var $width = $myWindow.width();
if ($width > 870) {
console.log('hey im 870');
$('#the-team-wrapper .flex-content').empty();
ajax_results.done(function(data) {
// console.log(data.job_titles[3]);
var employee_job_titles;
function job_titles_display(jobtitle,ind){
if (jobtitle.job_titles[ind].length>1) {
var my_array = [];
for (var i = 0; i < jobtitle.job_titles[ind].length; i++){
my_array.push(jobtitle.job_titles[ind][i][0]['title']);
employee_job_titles = my_array.join(' | ');
}
}else {
var employee_job_titles;
employee_job_titles = jobtitle.job_titles[ind][0]['title'];
}
return employee_job_titles;
}
for (var i = 0; i < data.employee_info.length; i++) {
if(i%2 == 0){
$('#the-team-wrapper .flex-content').append('<div class="profile-parent"><div class="employee-profile-pic flex-item" data-id="'+data.employee_info[i]['id']+'"></div><div class="employee-bio-wrapper flex-item"><h2 data-id="'+data.employee_info[i]['id']+'">'+data.employee_info[i]['firstname']+" "+data.employee_info[i]['lastname']+'</h2><h3 data-id="'+data.employee_info[i]['id']+'">'+job_titles_display(data,i)+
'</h3><p class="employee-bio-text employee-bio-text-not-active">'+data.employee_info[i]['bio']+'</p></div><button type="button" class="bio-prev-butt-left">View '+data.employee_info[i]['firstname']+'\'s'+' Bio</button><div class="hide-bio-close-button-left">x</div></div>');
}else {
$('#the-team-wrapper .flex-content').append('<div class="profile-parent"><div class="employee-bio-wrapper flex-item"><h2 data-id="'+data.employee_info[i]['id']+'">'+data.employee_info[i]['firstname']+" "+data.employee_info[i]['lastname']+'</h2><h3 data-id="'+data.employee_info[i]['id']+'">'+job_titles_display(data,i)+'</h3 data-id="'+data.employee_info[i]['id']+
'"><p class="employee-bio-text employee-bio-text-not-active">'+data.employee_info[i]['bio']+'</p></div><div class="employee-profile-pic flex-item" data-id="'+data.employee_info[i]['id']+'"></div><button type="button" class="bio-prev-butt-right">View '+data.employee_info[i]['firstname']+'\'s'+' Bio</button><div class="hide-bio-close-button-right">x</div></div>');
}
var profile_pic_path = data.employee_info[i]['profile_pic'].split('\\').join('\\\\');
$("#the-team-wrapper .flex-content-wrapper .flex-content .employee-profile-pic:eq("+i+")").css({'background': 'url(_employee_pics/'+profile_pic_path+')','background-repeat': 'no-repeat','background-position': 'center', 'background-size': 'cover'});
}
});
}
I have this code, and it should fire when width is greater than 870, but instead it fires when width is greater than 970 on Opera, and when width is about 890 on Chrome. How can I fix this and get consistent results across browsers. Thanks in advance.
Are you using a CSS reset to neutralize the browser's default margin or padding on the <body> element?
Different browsers add different amounts of either padding or margin to the <body> of the page, which could explain why the function is triggered at different points in different browsers.
The problem is, the resize event fires at different times and rates depending on browser, CPU load, and how fast you actually do the resizing.
Test the following code in your browsers. When I do this in a clean browser at a reasonable rate of coverage the difference usually comes in within around 2px of the target.
(BTW, you'll see I am caching the jQuery selectors into variables. Not strictly necessary for this test, but you might be surprised to find out how many bugs I've fixed because coders have invoked uncached jQuery selectors willy-nilly in loops and other repetitive places throughout their code).
var $window = $(window);
$window.on('resize',function(){
var w = $window.width();
if (w > 1000) {
console.log( w );
} else {
console.log('nope: ' + w)
}
});
Ok so I've revised the markup/code to make it easier to understand. Using JavaScript I want to know how to create a text slider that changes a paragraph in html5 either "forwards" or "backwards" on click?
I only want one div to show at a time and the first div (div_1) needs to be visible at the beginning as a default setting. I also want to be able to add more text divs to it in the future. I'm new to JavaScript so I want to keep it as simple as possible.
I've had a go creating it in JavaScript which hasn't worked, I'm not sure if I'm going about this the right way.
<!DOCTYPE html>
<html lang="en">
<head>
<style type="text/css">
.showHide {
display: none;
}
</style>
<script type="text/javascript">
var sdivs = [document.getElementById("div_1"),
document.getElementById("div_2"),
document.getElementById("div_3"),
document.getElementById("div_4")];
function openDiv(x) {
//I need to keep div_1 open as a starting point
sdivs[0].style.display ="block";
var j;
for (var j = 0; j < sdivs.length; j++) {
if (j === x) {
continue;
}
else {
sdivs[j].style.display = "none";
}
}
}
</script>
<title>text</title>
</head>
<body>
forward
backwards
<div id="text_holder">
<div id="div_1" class="showHide">One</div>
<div id="div_2" class="showHide">Two</div>
<div id="div_3" class="showHide">Three</div>
<div id="div_4" class="showHide">Four</div>
</div>
</body>
</html>
When dealing with multiple elements like this, I've found CSS alone to be insufficient (though its brilliant for modifying simple hover states or whatever). This one method here is pretty simple and specific to this one set of markup (so modify as you see fit). More importantly - its to illustrate how to set up a simple javascript "class" to handle your logic.
http://jsfiddle.net/1z13qb58/
// use a module format to keep the DOM tidy
(function($){
// define vars
var _container;
var _blurbs;
var _blurbWidth;
var _index;
var _clicks;
// initialize app
function init(){
console.log('init');
// initialize vars
_container = $('#text_holder .inner');
_blurbs = $('.blurb');
_blurbWidth = $(_blurbs[0]).innerWidth();
_clicks = $('.clicks');
_index = 0;
// assign handlers and start
styles();
addEventHandlers();
}
// initialize styles
function styles(){
_container.width(_blurbs.length * _blurbWidth);
}
// catch user interaction
function addEventHandlers(){
_clicks.on({
'click': function(el, args){
captureClicks( $(this).attr('id') );
}
});
}
// iterate _index based on click term
function captureClicks(term){
switch(term){
case 'forwards':
_index++;
if(_index > _blurbs.length - 1){
_index = 0;
}
break;
case 'backwards':
_index--;
if(_index < 0){
_index = _blurbs.length - 1;
}
break;
}
updateView();
}
// update the _container elements left value
function updateView(){
//_container.animate({
//'left' : (_index * _blurbWidth) * -1
//}, 500);
_container.css('left', ((_index * _blurbWidth) * -1) + 'px');
}
init();
})(jQuery);
I'm using jQuery to handle event binding and animation, but, again - there are lots of options (including a combination of vanilla javascript and CSS3 transitions).
I'll note also that this is all html4 and css2 (save your doctype).
Hopefully that helps -
I have a javascript function (epoch calendar) which displays a calendar when focus is set on certain text boxes. this works fine in ie8, ff (all versions as far as I can test), opera etc but doesn't work in ie7 or previous.
If i have it set up in a blank html test page it will work so I'm fairly sure it's a conflict with my css (provided to me by a designer).
I've traced the error to these lines of code -
Epoch.prototype.getTop = function (element) //PRIVATE: returns the absolute Top value of element, in pixels
{
var oNode = element;
var iTop = 0;
while(oNode.tagName != 'BODY') {
iTop += oNode.offsetTop;
oNode = oNode.offsetParent;
}
return iTop;
};
Epoch.prototype.getLeft = function (element) //PRIVATE: returns the absolute Left value of element, in pixels
{
var oNode = element;
var iLeft = 0;
while(oNode.tagName != 'BODY') {
iLeft += oNode.offsetLeft;
oNode = oNode.offsetParent;
}
return iLeft;
};
More specifically, if i remove the actual while loops then the calendar will display OK, just that its positioning on the page is wrong?
EDIT
Code below which sets 'element'
<script type="text/javascript">
window.onload = function() {
var bas_cal, dp_cal, ms_cal;
dp_cal = new Epoch('epoch_popup', 'popup', document.getElementById('<%=txtDateOfDiag.ClientID%>'));
dp_cal = new Epoch('epoch_popup', 'popup', document.getElementById('<%=txtDOB.ClientID%>'));
};
</script>
Note: I am using asp.net Master pages which is why there is a need for the .ClientID
EDIT
A further update - I have recreated this without applying css (but including the .js file provided by the designer) the code still works fine which, there must be some sort of conflict between the CSS and my JavaScript?
That would lead me to believe that the tagName does not match, possibly because you have it in upper case. You might try while(!oNode.tagName.match(/body/i)) {
what happens if you add a line of debug code like this:
var oNode = element;
var iLeft = 0;
alert(oNode);
This might give different results in different browsers; I think it may be NULL for IE.
You may want to have a look at the code that provides the value of the 'element' parameter to see if there's a browser-dependant issue there.
I'm working on a web page where I'm making an AJAX call that returns a chunk of HTML like:
<div>
<!-- some html -->
<script type="text/javascript">
/** some javascript */
</script>
</div>
I'm inserting the whole thing into the DOM, but the JavaScript isn't being run. Is there a way to run it?
Some details: I can't control what's in the script block (so I can't change it to a function that could be called), I just need the whole block to be executed. I can't call eval on the response because the JavaScript is within a larger block of HTML. I could do some kind of regex to separate out the JavaScript and then call eval on it, but that's pretty yucky. Anyone know a better way?
Script added by setting the innerHTML property of an element doesn't get executed. Try creating a new div, setting its innerHTML, then adding this new div to the DOM. For example:
<html>
<head>
<script type='text/javascript'>
function addScript()
{
var str = "<script>alert('i am here');<\/script>";
var newdiv = document.createElement('div');
newdiv.innerHTML = str;
document.getElementById('target').appendChild(newdiv);
}
</script>
</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
<div>hello world</div>
<div id="target"></div>
</body>
</html>
You don't have to use regex if you are using the response to fill a div or something. You can use getElementsByTagName.
div.innerHTML = response;
var scripts = div.getElementsByTagName('script');
for (var ix = 0; ix < scripts.length; ix++) {
eval(scripts[ix].text);
}
While the accepted answer from #Ed. does not work on current versions of Firefox, Google Chrome or Safari browsers I managed to adept his example in order to invoke dynamically added scripts.
The necessary changes are only in the way scripts are added to DOM. Instead of adding it as innerHTML the trick was to create a new script element and add the actual script content as innerHTML to the created element and then append the script element to the actual target.
<html>
<head>
<script type='text/javascript'>
function addScript()
{
var newdiv = document.createElement('div');
var p = document.createElement('p');
p.innerHTML = "Dynamically added text";
newdiv.appendChild(p);
var script = document.createElement('script');
script.innerHTML = "alert('i am here');";
newdiv.appendChild(script);
document.getElementById('target').appendChild(newdiv);
}
</script>
</head>
<body>
<input type="button" value="add script" onclick="addScript()"/>
<div>hello world</div>
<div id="target"></div>
</body>
</html>
This works for me on Firefox 42, Google Chrome 48 and Safari 9.0.3
An alternative is to not just dump the return from the Ajax call into the DOM using InnerHTML.
You can insert each node dynamically, and then the script will run.
Otherwise, the browser just assumes you are inserting a text node, and ignores the scripts.
Using Eval is rather evil, because it requires another instance of the Javascript VM to be fired up and JIT the passed string.
The best method would probably be to identify and eval the contents of the script block directly via the DOM.
I would be careful though.. if you are implementing this to overcome a limitation of some off site call you are opening up a security hole.
Whatever you implement could be exploited for XSS.
You can use one of the popular Ajax libraries that do this for you natively. I like Prototype. You can just add evalScripts:true as part of your Ajax call and it happens automagically.
For those who like to live dangerously:
// This is the HTML with script element(s) we want to inject
var newHtml = '<b>After!</b>\r\n<' +
'script>\r\nchangeColorEverySecond();\r\n</' +
'script>';
// Here, we separate the script tags from the non-script HTML
var parts = separateScriptElementsFromHtml(newHtml);
function separateScriptElementsFromHtml(fullHtmlString) {
var inner = [], outer = [], m;
while (m = /<script>([^<]*)<\/script>/gi.exec(fullHtmlString)) {
outer.push(fullHtmlString.substr(0, m.index));
inner.push(m[1]);
fullHtmlString = fullHtmlString.substr(m.index + m[0].length);
}
outer.push(fullHtmlString);
return {
html: outer.join('\r\n'),
js: inner.join('\r\n')
};
}
// In 2 seconds, inject the new HTML, and run the JS
setTimeout(function(){
document.getElementsByTagName('P')[0].innerHTML = parts.html;
eval(parts.js);
}, 2000);
// This is the function inside the script tag
function changeColorEverySecond() {
document.getElementsByTagName('p')[0].style.color = getRandomColor();
setTimeout(changeColorEverySecond, 1000);
}
// Here is a fun fun function copied from:
// https://stackoverflow.com/a/1484514/2413712
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
<p>Before</p>