javascript get natural width/height of all images in table - javascript

I am trying to get the actual image sizes from the images listed in column Image and display it in column Image Size.
The problem I have is that I am only able to get the size of the first image, which is added in each cell for column Image Size.
jsFiddle: https://jsfiddle.net/a92ck0em/
var xmlFile = 'https://raw.githubusercontent.com/joenbergen/files/master/XMLParse2.xml';
function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", xmlFile, true);
xhttp.send();
xhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
xmlFunction(this.response);
}
};
}
function xmlFunction(xml) {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(xml, "text/xml");
var table = "<tr><th>Category</th><th>Title</th><th>Image</th><th>Image Size</th></tr>";
var x = xmlDoc.getElementsByTagName("ITEM");
for (var elem of x) {
var titles = elem.getElementsByTagName(
"TITLE")[0].childNodes[0].nodeValue;
var cats = elem.getElementsByTagName("CATEGORY")[0].childNodes[0].nodeValue;
var imageURL = elem.getElementsByTagName("IMAGE").length === 0 ? "..." : elem.getElementsByTagName("IMAGE")[0].getAttribute('url');
var imageSize = elem.getElementsByTagName("IMAGE").length === 0 ? "..." : elem.getElementsByTagName("IMAGE")[0].width + 'x' + [0].height;
table += "<tr><td>" + cats + "</td><td>" + titles + "</td><td>" + "<img src=" + imageURL + ' height="150" width="100">' + '</td><td id="cellId"><textTag>' + "" + "</textTag></td></tr>";
}
document.getElementById("myTable").innerHTML = table;
document.querySelector("img").addEventListener('load', function() {
var imgTags = document.querySelectorAll('img'), i;
for (i = 0; i < imgTags.length; ++i) {
var image_width = document.querySelector("img").naturalWidth;
var image_height = document.querySelector("img").naturalHeight;
}
$('#myTable textTag').each(function() {
$(this).append(image_width + 'x' + image_height);
//console.log(image_width + 'x' + image_height);
});
});
}
table,
th,
td {
border: 1px solid black;
border-collapse: collapse;
}
th,
td {
padding: 5px;
}
<head>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<button type="button" onclick="loadDoc()">Load</button>
<br><br>
<table id="myTable"></table>
Expected:

Ok there are many parts to your question, I started doing the simplest thing, logging that parsed xml document, looks like this:
...
<IMAGE url="...."></IMAGE>
<IMAGESIZE></IMAGESIZE>
...
So in your script you are getting an attribute that does not exist. So you can safely remove this line:
var imageSize = elem.getElementsByTagName("IMAGE").length === 0 ? "..." : elem.getElementsByTagName("IMAGE")[0].width + 'x' + [0].height;
Next you need to check if the images are loaded, simplest thing to do:
"<img src=" + imageURL + ' height="150" width="100" onload="this._loaded = true;">' +
this will add a proprietary _loaded property to it.
Next you need to check when the images are loaded and once loaded fire a function, you need a recursive function to do that which does not blow the stack:
var imgTags = document.getElementsByTagName('img');
function isLoaded(){
if(Array.prototype.slice.call(imgTags).some(function(d,i){return !d._loaded})){
setTimeout(isLoaded,4);
} else {
$('#myTable textTag').each(function(i,node) {
node.textContent = imgTags[i].naturalWidth + 'x' + imgTags[i].naturalHeight;
//console.log(image_width + 'x' + image_height);
});
}
};
isLoaded();
I removed the querySelectorAll(img) bit, it is slower compared to getElementsByTagName and does not return a LIVE HTML Collection, returns a NodeList. The loaded function will fire your Jquery thing once it detects all the _loaded properties. The Array.prototype.slice.call is an old school way to convert HTML Collection or NodeList to a regular array, the cool kids you Array.from nowadays, it is up to you. You might also optimize the above function a bit by storing the result of Array.prototype.slice.call... once, I leave that to you. All in all it looks like this:
https://jsfiddle.net/ibowankenobi/h9evc81a/

The problem is that you need to wait for the images to load before querying properties like width or height.
Normally this is done by setting an onload event handler that updates the display of these properties.
Moreover you're making a loop setting two variables and then, outside of the loop, you're assigning all the images the value of the very same variables.
You need to do the querying inside the second loop instead.

Related

Can't add together two variables which store an offsetHeight

I'm trying to add two numbers together so I can use a CSS calc function to reduce the top margin of an element so it shows just the title and its top line text.
var roomInfoPanel = document.querySelector(".room-info");
var panelCapacityInfo = parseInt(document.querySelector(".room-info__capacity").offsetHeight);
var panelTitleRoom = parseInt(document.querySelector(".room-info__title").offsetHeight);
var panelCombinedHeights = panelCapacityInfo + panelTitleRoom;
if (panelTitleRoom && panelCapacityInfo) {
document.querySelector(".room-info").style.marginTop = "calc( -" + panelCombinedHeights + "-7vw + -20px)";
}
However, panelCombinedHeights always returns undefined :(
I don't think this is a scope issue.
The parseInt was added after looking at this article, but without this no good either.
What am I doing wrong?
Try to convert variables to numbers
var roomInfoPanel = document.querySelector(".room-info");
var panelCapacityInfo = parseInt(document.querySelector(".room-info__capacity").offsetHeight);
var panelTitleRoom = parseInt(document.querySelector(".room-info__title").offsetHeight);
// Convert variables to numbers
// Print what to summ
console.log(panelCapacityInfo, panelTitleRoom)
var panelCombinedHeights = Number(panelCapacityInfo) + Number(panelTitleRoom);
// Print summ
console.log(panelCombinedHeights)
if (panelTitleRoom && panelCapacityInfo) {
console.log("Style to be applied:", "calc( -" + panelCombinedHeights + "-7vw + -20px)");
document.querySelector(".room-info").style.marginTop = "calc( -" + panelCombinedHeights + "-7vw + -20px)";
}
.room-info {
height: 200px;
}
.room-info__capacity {
height: 100px;
}
.room-info__title {
height: 70px;
}
<div class="room-info">
<div class="room-info__capacity">
1
</div>
<div class="room-info__title">
2
</div>
</div>
The function offsetHeigth already returns a number, so you don't need the parseInt() here.
Are the values panelCapacityInfo and panelTitleRoom defined or not?
If they are undefined then the error is probably in the querySelector.
var roomInfoPanel = document.querySelector(".room-info");
var panelCapacityInfo = parseInt(document.querySelector(".room-info__capacity").offsetHeight);
var panelTitleRoom = parseInt(document.querySelector(".room-info__title").offsetHeight);
var panelCombinedHeights = parseInt(panelCapacityInfo) + parseInt(panelCapacityInfo)
if (panelTitleRoom && panelCapacityInfo) {
document.querySelector(".room-info").style.marginTop = "calc( -" + panelCombinedHeights + "-7vw + -20px)";
}

Changing linear-gradient with Javascript not working

I'm trying to change the linear gradient with JavaScript in my project. My problem is that after running the code nothing happens. If I try to use the linear-gradient string directly in my CSS class container, everything works fine but it doesn't work with changing it via JavaScript. Here's the code I'm trying to run:
function init() {
var Lohn = "undefined";
var Datum = "undefined";
var Pausenstueck = "undefined";
var Zakstueck = "undefined";
var Ueberstundenstueck = "undefined";
var StundeKoord = "undefined";
var Tagesdauer = "undefined";
let UebersichtTemplate = document.createElement("dd");
let UebersichtDD = document.getElementById("Uebersicht_Window_child");
UebersichtTemplate.innerHTML = "<span class='textRight'>" + Lohn + "</span><span class='text'>" + Datum + "</span>";
let a = document.importNode( UebersichtTemplate,true);
a.classList.add("percentage", "percentage-" + Math.round(StundeKoord*Tagesdauer));
let colorString = "linear-gradient(to right, #9c9e9f 0%,#9c9e9f " + Pausenstueck + Zakstueck + Ueberstundenstueck + " 100%);"
UebersichtDD.appendChild(a);
a.style.backgroundImage = '-webkit-linear-gradient(to right, #9c9e9f 0%,#9c9e9f 50%, #F53323 50%, #F53323 100%)';
}
document.addEventListener("DOMContentLoaded", init);
<body>
<div id="Uebersicht_Window_child"></div>
</body>
I already tried using double quotes instead of single quotes but it also doesn't work. leaving the "-webkit-" doesn't solve my problem either... I absolutely don't know the problem. Hope you guys could help me :)
Thank you!
There is one major issue: You never actually insert the dd element that you imported into the document:
From Document​.import​Node():
The Document object's importNode() method creates a copy of a Node or
DocumentFragment from another document, to be inserted into the
current document later.
(Emphasis mine)
There are a couple of other issues, mainly around code structure and style - but they may not be pertinent to the core question - but also syntax problems with creating the colorString that may have been contributing.
function init() {
var Lohn = "undefined";
var Datum = "undefined";
var StundeKoord = "undefined";
var Tagesdauer = "undefined";
// -----------------------------
var Pausenstueck = "#9c9e9f 50%";
var Zakstueck = "#F53323 50%";
var Ueberstundenstueck = "#F53323 100%";
let UebersichtTemplate = document.createElement("dd");
UebersichtTemplate.innerHTML = "<span class='textRight'>" + Lohn + "</span>" +
"<span class='text'>" + Datum + "</span>";
let a = document.importNode( UebersichtTemplate, true );
a.classList.add( "percentage", "percentage-" + Math.round(StundeKoord*Tagesdauer) );
a.style.backgroundImage = `-webkit-linear-gradient(top right, #9c9e9f 0%, ${Pausenstueck}, ${Zakstueck}, ${Ueberstundenstueck} )`;
// insert the imported node.
document.body.appendChild(a);
}
document.addEventListener("DOMContentLoaded", init);
<body>
<div id="Uebersicht_Window_child"></div>
</body>

Styling Elements that have been appended using jquery

I have div elements that are appended on a parent div. Depending on the screen size, the div elements should resize but my efforts to do so using jquery .css() are failing all through. Kindly help. Below is a sample of my code
var len = data.length; //a number comes here
var i;
for(i=1;i<len+1;i++){
$(".app-body").append(
"<div class='list-box' onclick='OpenHymn("+ i +")'>" +
"<div class='circle-text'><div class='hym_num'>" + i + "</div></div>" +
"</div>");
}
//calling the function that should resize the divs
GridBoxes();
function GridBoxes(){
var width=window.innerWidth;
var divisible=parseInt(width-40);
var size=divisible/4;
var boxes=$('.list-box');
boxes.css("width",size+"px");
boxes.css("height",size+"px");
}
Put the function inside $(window).resize() for it to fire everytime the window is resized
Code:
$(window).resize(function() {
GridBoxes();
})
Example
You should really use media query's for this kind of problem.
try this
window.addEventListener("resize", GridBoxes);//called on window resize
var len = 4; //changed to a number here for testing
var i;
for(i=1;i<len+1;i++){
$(".app-body").append(
"<div class='list-box' onclick='OpenHymn("+ i +")'>" +
"<div class='circle-text'><div class='hym_num'>" + i + "</div></div>" +
"</div>");
}
//calling the function that should resize the divs
//GridBoxes();
function GridBoxes(){
var width=window.innerWidth;
var divisible=parseInt(width-40);
var size=divisible/4;
var boxes=$('.list-box');
for(var b=0;b<boxes.length;b++){
boxes[b].css("width",size+"px");
boxes[b].css("height",size+"px");
}
}
.list-box{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="app-body"></div>

Text pagination inside a DIV with image

I want to paginate a text in some div so it will fit the allowed area
Logic is pretty simple:
1. split text into words
2. add word by word into and calculate element height
3. if we exceed the height - create next page
It works quite good
here is JS function i've used:
function paginate() {
var newPage = $('<pre class="text-page" />');
contentBox.empty().append(newPage);
var betterPageText='';
var pageNum = 0;
var isNewPage = false;
var lineHeight = parseInt(contentBox.css('line-height'), 10);
var wantedHeight = contentBox.height() - lineHeight;
for (var i = 0; i < words.length; i++) {
if (isNewPage) {
isNewPage = false;
} else {
betterPageText = betterPageText + ' ' + words[i];
}
newPage.text(betterPageText + ' ...');
if (newPage.height() >= wantedHeight) {
pageNum++;
if (pageNum > 0) {
betterPageText = betterPageText + ' ...';
}
newPage.text(betterPageText);
newPage.clone().insertBefore(newPage)
betterPageText = '...';
isNewPage = true;
} else {
newPage.text(betterPageText);
}
}
contentBox.craftyslide({ height: wantedHeight });
}
But when i add an image it break everything. In this case text overflows 'green' area.
Working fiddle: http://jsfiddle.net/74W4N/7/
Is there a better way to paginate the text and calculate element height?
Except the fact that there are many more variables to calculate,not just only the word width & height, but also new lines,margins paddings and how each browser outputs everything.
Then by adding an image (almost impossible if the image is higher or larger as the max width or height) if it's smaller it also has margins/paddings. and it could start at the end of a line and so break up everything again.basically only on the first page you could add an image simply by calculating it's width+margin and height+margin/lineheight. but that needs alot math to get the wanted result.
Said that i tried some time ago to write a similar script but stopped cause of to many problems and different browser results.
Now reading your question i came across something that i read some time ago:
-webkit-column-count
so i made a different approach of your function that leaves out all this calculations.
don't judge the code as i wrote it just now.(i tested on chrome, other browsers need different prefixes.)
var div=document.getElementsByTagName('div')[0].firstChild,
maxWidth=300,
maxHeigth=200,
div.style.width=maxWidth+'px';
currentHeight=div.offsetHeight;
columns=Math.ceil(currentHeight/maxHeigth);
div.style['-webkit-column-count']=columns;
div.style.width=(maxWidth*columns)+'px';
div.style['-webkit-transition']='all 700ms ease';
div.style['-webkit-column-gap']='0px';
//if you change the column-gap you need to
//add padding before calculating the normal div.
//also the line height should be an integer that
// is divisible of the max height
here is an Example
http://jsfiddle.net/HNF3d/10/
adding an image smaller than the max height & width in the first page would not mess up everything.
and it looks like it's supported by all modern browsers now.(with the correct prefixes)
In my experience, trying to calculate and reposition text in HTML is almost an exercise in futility. There are too many variations among browsers, operating systems, and font issues.
My suggestion would be to take advantage of the overflow CSS property. This, combined with using em sizing for heights, should allow you to define a div block that only shows a defined number of lines (regardless of the size and type of the font). Combine this with a bit of javascript to scroll the containing div element, and you have pagination.
I've hacked together a quick proof of concept in JSFiddle, which you can see here: http://jsfiddle.net/8CMzY/1/
It's missing a previous button and a way of showing the number of pages, but these should be very simple additions.
EDIT: I originally linked to the wrong version for the JSFiddle concept
Solved by using jQuery.clone() method and performing all calculations on hidden copy of original HTML element
function paginate() {
var section = $('.section');
var cloneSection = section.clone().insertAfter(section).css({ position: 'absolute', left: -9999, width: section.width(), zIndex: -999 });
cloneSection.css({ width: section.width() });
var descBox = cloneSection.find('.holder-description').css({ height: 'auto' });
var newPage = $('<pre class="text-page" />');
contentBox.empty();
descBox.empty();
var betterPageText = '';
var pageNum = 0;
var isNewPage = false;
var lineHeight = parseInt(contentBox.css('line-height'), 10);
var wantedHeight = contentBox.height() - lineHeight;
var oldText = '';
for (var i = 0; i < words.length; i++) {
if (isNewPage) {
isNewPage = false;
descBox.empty();
}
betterPageText = betterPageText + ' ' + words[i];
oldText = betterPageText;
descBox.text(betterPageText + ' ...');
if (descBox.height() >= wantedHeight) {
if (i != words.length - 1) {
pageNum++;
if (pageNum > 0) {
betterPageText = betterPageText + ' ...';
}
oldText += ' ... ';
}
newPage.text(oldText);
newPage.clone().appendTo(contentBox);
betterPageText = '... ';
isNewPage = true;
} else {
descBox.text(betterPageText);
if (i == words.length - 1) {
newPage.text(betterPageText).appendTo(contentBox);
}
}
}
if (pageNum > 0) {
contentBox.craftyslide({ height: wantedHeight });
}
cloneSection.remove();
}
live demo: http://jsfiddle.net/74W4N/19/
I actually came to an easier solution based on what #cocco has done, which also works in IE9.
For me it was important to keep the backward compatibility and the animation and so on was irrelevant so I stripped them down. You can see it here: http://jsfiddle.net/HNF3d/63/
heart of it is the fact that I dont limit height and present horizontal pagination as vertical.
var parentDiv = div = document.getElementsByTagName('div')[0];
var div = parentDiv.firstChild,
maxWidth = 300,
maxHeigth = 200,
t = function (e) {
div.style.webkitTransform = 'translate(0,-' + ((e.target.textContent * 1 - 1) * maxHeigth) + 'px)';
div.style["-ms-transform"] = 'translate(0,-' + ((e.target.textContent * 1 - 1) * maxHeigth) + 'px)';
};
div.style.width = maxWidth + 'px';
currentHeight = div.offsetHeight;
columns = Math.ceil(currentHeight / maxHeigth);
links = [];
while (columns--) {
links[columns] = '<span>' + (columns + 1) + '</span>';
}
var l = document.createElement('div');
l.innerHTML = links.join('');
l.onclick = t;
document.body.appendChild(l)

changing background image with a for loop

i have a table with 3 cells the middel 1 in a black image so it will look like there is a line in the middle of the screen.
now in the other cell i want to show pictures, so i tryed to do a loop that changing the images every second with by hiding the cells and then show them.
the script:
$(window).ready(function () {
//the images sits in a div with a hidden property.
var AlumniumPictures = $("#AlumnimPictureHolder").children();
var ShipozimPictures = $("#ShipozimPictureHolder").children();
//var timer = $.timer(yourfunction, 10000);
for (var i = 0; i < 10; i++) {
$(".almoniyomButtonTD").css({
"background-image": "url(" + $(AlumniumPictures[i]).attr('src') + ")"
});
$(".shipozimButtonTD").css({
"background-image": "url(" + $(ShipozimPictures[i]).attr('src') + ")"
});
$(".almoniyomButtonTD").hide();
$(".shipozimButtonTD").hide();
$(".almoniyomButtonTD").show(1100);
$(".shipozimButtonTD").show(1100);
//for some reson the code dosnt work if im not using the setInterval method.
document.setInterval(1000);
}
});
this is not working it only show me the first images and then stop.
is there a batter way to do this?
am im doing this right?
I think you might do this for the background:
$(window).ready(function () {
//the images sits in a div with a hidden property.
var AlumniumPictures = $("#AlumnimPictureHolder").children();
var ShipozimPictures = $("#ShipozimPictureHolder").children();
//var timer = $.timer(yourfunction, 10000);
time = 0;
step = 1000; // One secund
for (var i = 0; i < 10; i++) {
time+= step;
$(".almoniyomButtonTD").hide();
$(".shipozimButtonTD").hide();
$(".almoniyomButtonTD").show(1100);
$(".shipozimButtonTD").show(1100);
//for some reson the code dosnt work if im not using the setInterval method.
document.setInterval("changeBG('" + $(AlumniumPictures[i]).attr('src') + "', '.almoniyomButtonTD')", time);
document.setInterval("changeBG('" + $(AlumniumPictures[i]).attr('src') + "', '.shipozimButtonTD')", time);
}
});
function changeBG(image, obj) {
$(obj).css({
"background-image": "url(" + image + ")"
});
}
But I don't undestand what you want to do with this:
$(".almoniyomButtonTD").hide();
$(".shipozimButtonTD").hide();
$(".almoniyomButtonTD").show(1100);
$(".shipozimButtonTD").show(1100);
See the docs about setInterval. You need to tell it what code you are running.
window.setInterval(code, delay);
You aren't specifying any code for it to run! Try placing your for statement in a function and calling that.
Also, from Mozilla and MS docs setInterval seems to be on the window object, not on the document object. I don't think it will work the way you have it. I imagine if you looked in a debugger you would see an error thrown.
window.setInterval(myFunction, 1000);
function myFunction() {
for (var i = 0; i < 10; i++) {
$(".almoniyomButtonTD").css({
"background-image": "url(" + $(AlumniumPictures[i]).attr('src') + ")"
});
$(".shipozimButtonTD").css({
"background-image": "url(" + $(ShipozimPictures[i]).attr('src') + ")"
});
$(".almoniyomButtonTD").hide();
$(".shipozimButtonTD").hide();
$(".almoniyomButtonTD").show(1100);
$(".shipozimButtonTD").show(1100);
}
}

Categories