What Is Wrong With This Javascript Object Code? - javascript

var Detector = function() {
var baseFonts = ['monospace', 'sans-serif', 'serif'];
var testString = "mmmmmmmmmmlli";
var testSize = '72px';
var h = document.getElementsByTagName("body")[0];
// create a SPAN in the document to get the width of the text we use to test
var s = document.createElement("span");
s.style.fontSize = testSize;
s.innerHTML = testString;
var defaultWidth = {};
var defaultHeight = {};
for (var index in baseFonts) {
//get the default width for the three base fonts
s.style.fontFamily = baseFonts[index];
h.appendChild(s);
defaultWidth[baseFonts[index]] = s.offsetWidth; //width for the default font
defaultHeight[baseFonts[index]] = s.offsetHeight; //height for the defualt font
h.removeChild(s);
}
function detect(font) {
var detected = false;
for (var index in baseFonts) {
s.style.fontFamily = font + ',' + baseFonts[index]; // name of the font along with the base font for fallback.
h.appendChild(s);
var matched = (s.offsetWidth != defaultWidth[baseFonts[index]] || s.offsetHeight != defaultHeight[baseFonts[index]]);
h.removeChild(s);
detected = detected || matched;
}
return detected;
}
this.detect = detect;
};
var d = new Detector();
alert(d.detect("Times"));
//downloaded from http://www.lalit.org/lab/javascript-css-font-detect/
This checks for fonts installed on the system by indirect method of implications. Somehow it was working perfectly on my webpage at first. I added in some more coding and now it has stopped working. I have removed all that coding and reverted the coding to that which was working initially, but it is still not working. I have tried copy-pasting it to some other pages and it is still not working there either. But when I post all this code as text and run an eval() on it, suddenly it starts working. I'm going mad. Can somebody please resolve it?

Would it not be easier just to use a font fallback within your CSS? Seems like an awful lot of JavaScript to do something that CSS can do in one line. That is assuming it's for a practical purpose.

OK I stumbled upon the real issue randomly. It keeps happening to me all the time when I use DOM references in my javascript. It's pretty little thing and is easily overlooked but thats what it makes all the more annoying and hard to catch.
I was using this code in the head section, before the body tag. Cutting it from there and pasting it after the body code resolved the issue. The reason appears to be that since this code tries to create a span object inside the body section, using this code before the body tag will end up in a logical error because by that time, the body object doesn't exist. Relocating the code beyond the body section rectified the issue and the code is running smoothly now. Thanks to all who contributed.

Related

performant way to see if any element has a background-image

Short version of the question
Is it possible to find only elements on a page that have a background-image or background: url set (including in stylesheets) without looping through every element on the page and using getComputedStyle(el);.
If not is it possible to optimise the elements I look through to reduce JS execution time?
Longer version of the question
As part of this related question I am trying to find a solution to gathering the size of all elements above the fold that may impact the "visually complete" state of the page.
The related question covers checking all CSS etc. is loaded so I am left with images (including background images) to check.
I am looking to make the following functions as performant as possible (as I may have to call it multiple times if I am unable to solve the main problem in the other question).
The main function is getRects(). I have included the checkRectangle function for completeness but the main concern is the way I am gathering candidates for the checkRectangle function (having to loop through every element on the page).
var doc = window.document;
var browserWidth = window.innerWidth || doc.documentElement.clientWidth;
var browserHeight = window.innerHeight || doc.documentElement.clientHeight;
function checkRectangle(el){
var rtrn = false;
if (el.getBoundingClientRect) {
var rect = el.getBoundingClientRect();
//check if the bottom is above the top to ensure the element has height, same for width.
//Then the last 4 checks are to see if the element is in the above the fold viewport.
if (rect.bottom <= rect.top || rect.right <= rect.left || rect.right < 0 || rect.left > browserWidth || rect.bottom < 0 || rect.top > browserHeight) {
rtrn = false;
}else{
rtrn = {};
rtrn.bot = rect.bottom;
rtrn.top = rect.top;
rtrn.left = rect.left;
rtrn.right = rect.right;
}
}
return rtrn;
}
//function to get the rectangles above the fold (I do other things to check fonts are loaded etc. so images are the only thing left to check)
function getRects(){
var rects = [];
var elements = doc.getElementsByTagName('*');
var re = /url\(.*(http.*)\)/ig;
for (var i = 0; i < elements.length; i++) {
var el = elements[i];
var style = getComputedStyle(el);
if(el.tagName == "IMG"){
var rect = checkRectangle(el);
if(rect){
//The URL is stored here for later processing where I match performance timings to the element, it is not relevant other than to show why I convert the `getBoundingClientRect()` to a simple object.
rect.url = el.src;
rects.push(rect);
}
}
//I also need to check for background images set in either CSS or with inline styles.
if (style['background-image']) {
var rect = checkRectangle(el);
if(rect){
var matches = re.exec(style['background-image']);
if (matches && matches.length > 1){
rect.url = matches[1].replace('"', '');
rects.push(rect);
}
}
}
}
Concerns / things that I can't work out
I see no way of not looping through all elements on the page and using getComputedStyle(el) to check if they have a background-image set. If I can reduce the candidates sufficiently that would solve my problems.
At the moment (due to having to call the function multiple times) I am not doing a check for background: url but that needs adding in as an efficient way as possible.
Is there a way of discarding some elements on the page that I can guarantee are not "above the fold" that wouldn't carry a massive performance penalty (bearing in mind anything could be position: fixed at the top of the page?).
Things I know I can do
If I can find a better way of checking for background and background-image then I know images become easier as I can use querySelectorAll and limit that list.
Additional information / thoughts
I am already tracking every network request using PerformanceObserver.
Is there perhaps a way I could look at every request, grab the file name if it is an image and then use the filename to work out where that image is displayed on the page, even if it is a background-image or background: url set in external CSS?
Alternative way of phrasing the question.
How could I possibly limit a list of elements that can make a network call for an image and how can I then check if they are above the fold as efficiently as possible?

Photoshop Javascript to go to the next layer (layer above)

I want to be able to go to the layer above. I have over 3000 files that i need to select the layer above from and I cannot seem to work out how to do it.
It always has a different name too. But i always start from the same layer and it's always in the same position.
I need this to get the contents of a text layer.
Any ideas? I've been at it a while now but my Javascript knowledge is limited.
Thanks
There might be a smarter way to do this, but the following should work. You can basically tell the layer's z-position by its itemIndex property. So once you have that you can search the one with an itemIndex which is one higher than the current one. When you found it, you can make sure it's a text layer and if so, retrieve it's text contents.
var textContent = "";
var doc = app.activeDocument;
var ix = doc.activeLayer.itemIndex;
for(var i = 0; i < doc.layers.length; i++) {
if(doc.layers[i].itemIndex === ix + 1 && doc.layers[i].kind === LayerKind.TEXT) {
textContent = doc.layers[i].textItem.contents;
break;
}
}
alert("The text content is: " + textContent);

Using Javascript getBoundingClientRect to Snap Items to Grid

EDIT: I've simplified the code (below and in fiddle) down to the major problem needed to be solved in hope of creating more readability.
I've implemented Bader's solution for correctly using getBoundingClientRect value and using document.querySelector for getting both the class name and the html tag needed for the function. I'd now like to move on to the last five lines of the code beginning with var = style.
I've now corrected the math for the final two variables.
→ I'm trying to achieve creating a snapping function for use alongside Plumber, a baseline-grid Sass plugin.
Basically, I have a vertically centered flex item that needs to -- instead of being perfectly centered -- snap in an upward direction to the closest grid line. This will allow me to have a consistent vertical rhythm between slides in a custom mobile-based experience.
I'm using getBoundingClientRect to calculate the distance between the bottom of an object, and the top of the window.
Then I use Math.floor to round down to the nearest multiple of my rem value.
Then I use this new value to create a CSS bottom margin on the flex-centered container for the alignment fix.
(Then to finish, I'd like to have this function load on $(document).ready and on window resize.)
function() {
var box = document.querySelector('.box-1');
var rect = box.getBoundingClientRect();
var bottomOrig = rect.bottom;
var htmlRoot = document.querySelector('html');
var style = getComputedStyle(htmlRoot);
var remValue = style.getPropertyValue('font-size');
var bottomNew = Math.floor(bottomOrig / remValue) * remValue;
var fix = bottomOrig - bottomNew;
$('.container-2').css("margin-bottom", "fix + 'px'");
}
Here's the fiddle.
I most likely have a syntax problem here, and would greatly appreciate help.
Thanks!
Here are some errors / corrections.
GetBoundingClientRect() is a JS function, not jQuery, so it must be used on a javascript element, not a jquery selector. Using the [0] accessor on the jquery selector (if that's how you want to get it) will give you the JS element.
Also noticed that you were trying to select the "html" tag by id, but it doesn't have any Id. Changed it to getElementsByTagName.
var offsetYOrig = $('.box-1')[0].getBoundingClientRect().bottom;
// or, without jQuery:
// var offsetYOrig = document.getElementsByClassName('box-1')[0].getBoundingClientRect().bottom;
var html = document.getElementsByTagName("html")[0];
var style = window.getComputedStyle(html);
var remValue = style.getPropertyValue('font-size');
Edit: Regarding your edit, if you need to call the javascript to recompute on window resize, you may want to try something like this. I'm not sure if it achieves what you want fully (I don't completely understand your 'snapping' requirements, but this will at least call the code again. You may still have to edit the code in the snapFunction if it doesn't suit your needs.
I added some console logs that might help you check your math as it seemed a bit problematic to me, though I was unsure how to fix it because I don't understand your goal.
function snapFunction ()
{
var box = document.querySelector('.box-1');
var rect = box.getBoundingClientRect();
var bottomOrig = rect.bottom;
var htmlRoot = document.querySelector('html');
var style = getComputedStyle(htmlRoot);
var remValue = style.getPropertyValue('font-size');
var bottomNew = Math.floor(bottomOrig / remValue) * remValue;
var fix = bottomOrig - bottomNew;
// open your browser console and check the value of these to check your math and what values you're getting
console.log("bottomOrig: " + bottomOrig )
console.log("remValue: " + remValue)
console.log("bottomNew: " + bottomNew )
// note: no quotes around your variable name fix here
$('.container-2').css("margin-bottom", fix + "px");
};
// call on load
(function() {
snapFunction();
})();
// call on resize
$( window ).resize(function() {
snapFunction();
});
I did notice that the value of your bottomNew variable was logging as "NaN" (Not a Number) so I think something is going wrong there.
I think you're getting a font-size like "36px" instead of just "36". Maybe you could try
var remValue = parseInt(style.getPropertyValue('font-size'), 10);
The 10 in that parseInt function is just specifying we want to use base 10 numbers.
I hope this will help you
Here's the edited fiddle
jsfiddle.net/ztf64mwg/82/
I just edited some variables and fixed some of the errors
I ended up jumping on HackHands and, with help, came up with a great working solution.
This will snap any vertically flex-centered object to a grid with its size set as 1rem.
All you need to do is give the object that is being measured for distance the id attribute "measure", making sure that this object is aligned correctly with a 1rem grid from the top of its own container.
Then give the parent container (or any container higher in the DOM tree) that you'd like to snap to the grid the class of "snap".
If anyone ever finds a use for this and needs further explanation, just let me know.
function snap(object){
var rect = object.getBoundingClientRect();
var bottomOrig = rect.bottom;
var htmlRoot = document.querySelector('html');
var style = getComputedStyle(htmlRoot);
var remValue = parseInt(style.getPropertyValue('font-size'));
var bottomNew = Math.floor(bottomOrig / remValue) * remValue;
var topFixPositive = bottomNew - bottomOrig;
var topFixNegative = -Math.abs(topFixPositive);
$(object).closest('.snap').css("margin-top", topFixNegative);
}
function snapClear(object){
$(object).closest('.snap').css("margin-top", "0");
}
var measureHome = document.querySelector('#measure');
snap(measureHome);
$(window).on('resize', function() {
snapClear(measureHome);
snap(measureHome);
});

draw canvas lines to all divs with the same class name

I am making a document that will build a tree type graph through user input. I am trying to connect styled divs to the relative div they branched from with canvas lines.
I have been using .getBoundingClientRect() to get the positions, but the divs are static with inline-block, so every time a new one is added, the whole structure changes.
So, here is my attempt at a 'for loop' that is called every time a new branch is made, to re-draw all of the canvas lines.
var lines = function(){
var blocks=document.getElementsByClassName('block');
for (i=1;i<blocks.length-1;i++){
var blockDiv = blocks[i]
var offset = blockDiv.getBoundingClientRect();
var xa = offset.left+40;
var ya = offset.top+40;
var blockFrom = blockDiv.parentNode.parentNode.previousSibling;
var offsets = blockFrom.getBoundingClientRect();
var yb = offsets.top+40;
var xb = offsets.left+40;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.moveTo(xa,ya);
ctx.lineTo(xb,yb);
ctx.stroke();
}
}
Here is a jsfiddle so you can see the general structure of the divs.
When the function is called, I get no canvas lines and a console error of
166 Uncaught TypeError: blockDiv.parentNode.parentNode.previousSibling.getBoundingClientRect is not a function
I am stumped on this one and would really appreciate the help.
I am new to canvas, javascript, and coding in general so any other constructive criticism would also be greatly appreciated. :)
Vanilla js only please!
The problem is this:
Gecko-based browsers insert text nodes into a document to represent
whitespace in the source markup. Therefore a node obtained, for
example, using Node.firstChild or Node.previousSibling may refer to a
whitespace text node rather than the actual element the author
intended to get.
https://developer.mozilla.org/en-US/docs/Web/API/Node/previousSibling
Therefore, change this line:
var blockFrom = blockDiv.parentNode.parentNode.previousSibling;
to this:
var blockFrom = blockDiv.parentNode.parentNode.previousSibling.previousSibling;

javascript - incorrect viewport size

I'm try to get the browser viewport size.
When the page initially loads (in jQuery(function() { .. });) , both these show the correct value (eg: 560):
console.log($(window).height());
console.log(document.documentElement.clientHeight);
But later when I do the same thing, it shows the height of the whole docoument (eg: 11675).
There's a lot of HTML and JS and it would take a while to figure out what's going on, I was just wondering, did anyone see anything like this, if so, what can cause it and how can I get the correct size of the viewport? All google hits show that's the correct way to retrieve the value.
Note: I'm using chrome.
I recently bumped into the same problem in one of my projects. I didn't have time to dig and isolate this weird bug, and I ended up using this function (adapted from this answer) to correctly get the viewport dimensions :
var getViewportSize = (function(){
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0];
return function(){
return {
w : Math.max(w.innerWidth || e.clientWidth || g.clientWidth, app.config.minWidth),
h : Math.max(w.innerHeight|| e.clientHeight|| g.clientHeight, app.config.minHeight)
};
}
})();
From what I've tested, jQuery returned the incorrect size when the console or some other browser extension/toolbar was occupying some of the viewport space.
Hope this helps, but I'm also curious and trying to figure this one out, because it's hard to think that a mature lib such as jQuery 2.0 has these kind of bugs.

Categories

Resources