Convert absolute values ​​to relative CSS values ​​using Javascript

I was wondering if it is possible and possible to convert absolute CSS values ​​to relative while maintaining proportions using Javascript. The solution should work with HTML, as well as with SVG markup.

Say you got this:

<div style="width:100%;height:800px"> <svg width="1000" height="500"> <rect width="30" height="50" x="34" y="24"/> </svg> </div> 

How would you achieve this?

 <div style="width:100%;height:100%"> <svg width="83%" height="62.5%"> <rect width="3%" height="10%" x="3.4% y="4.8%"/> </svg> </div> 

I'd

  • start with root element ( div )
  • get .width() and .height() from this , and the parent element
  • calculate proportion ( this_height/parent_height*100 )
  • set width and height accordingly
  • iterate over children, set the child as root, start at 1.

What are the common traps that can occur? There are probably tags that don't have width and height, like svg:g . Then there are custom size definitions such as svg:circle or attributes that you don't think of first, like svg:text dy . Is there any way to make a reasonable assumption about the performance hit due to massive remelts? What else?

Thank you very much!

+6
source share
1 answer

Ok So this is what I understand what you want to do in your own words.

Scroll through all the DOM elements, converting the absolute sizes (i.e. height / width in pixels) to relative sizes (i.e. height / width in percent). This should be done with sensitivity as to whether the dimension is specified using CSS or HTML attributes so that they can be used for SVG elements.

There is one main limitation: if you want to convert absolute measurements to relative sizes using <x> as the root element, then <x> must have absolute sizes. Otherwise, the relative sizes inside the container will resize based on their contents instead of the total size of the parent <x> .

Given what you ask is pretty easy. This is the algorithm to use.

  • Start with <body>
  • Give <body> fixed sizes
  • Pave the DOM tree for <body> by doing the following for each element
    • Get element sizes
    • Get parent sizes
    • Convert to relative sizes (i.e. parent width / width).
    • Apply relative sizes

To test the code, I used the following HTML code. It contains both the code you include and the instance where the child is larger than the parent. This is one case that I have identified as a boundary condition for verification.

 <div style="width:100%;height:800px"> <svg width="150" height="200" style="background: red;"> <rect width="30" height="50" x="34" y="24"/> </svg> <div style="background: green; width: 50px; height: 50px;"> <p style="background: teal; width: 100px; height: 20px;">Content</p> </div> </div> 

This is JavaScript. He comments quite well and provides decent console work. For production code, I would recommend recommending that you delete console logging.

Please note that the code is not executed immediately, but with a 2 second delay. This way you can see what the DOM looks like before it changes.

For those who do not know that the following code will return a if it is present, not false and b otherwise.

 foo = a || b; 

We could rewrite it as one of two below, but it is more eloquent.

 foo = a ? a : b; if(a) foo = a else foo = b 

Here is jsFiddle .

And the code!

 /** convertToRelative * Convert absolute width/height to relative. Should work for * Both HTML and SVG objects. Tries to use CSS attributes, falls * back on HTML attributes. */ function convertToRelative(element) { // Get Element and Parent element = $(element); parent = element.parent(); // If there is no valid with, we need to use attributes var useAttr = !element.width(); // Get dimensions from css or attributes var width = element.width() || element.attr('width'); var height = element.height() || element.attr('height'); var pWidth = parent.width() || parent.attr('width'); var pHeight = parent.height() || parent.attr('height'); // Find relative dimensions var relWidth = (width / pWidth) * 100.0; var relHeight = (height / pHeight) * 100.0; // Log info console.log(element); console.log("1: "+ parent.height() +", 2: "+ parent.attr('height')); console.log("Width: "+ width +", Height: "+ height); console.log("PWidth: "+ pWidth +", pHeight: "+ pHeight); console.log("relWidth: "+ relWidth +", relHeight: "+ relHeight); // Clear current stylings element.removeAttr('width'); element.removeAttr('height'); // Apply relative dimensions if (useAttr) { element.attr('width', relWidth +'%'); element.attr('height', relHeight +'%'); } else { element.width(relWidth +"%"); element.height(relHeight +"%"); } } /** walk * Walk through a DOM element and all its sub elements running * the convertToRelative function to convert absolute positions * to relative positions. * DOES NOT CONVERT THE FIRST ELEMENT, ONLY CHILDREN. */ function walk(element) { $(element).children().each(function() { convertToRelative(this); walk(this); }); } /** fixBody * In order to play with relative dimensions for children of the * body, we need to fix the dimensions of the body. Otherwise it * will resize as we begin to use relative dimensions. */ function fixBody() { $('body').css('width', $('body').width() +"px"); $('body').css('height', $('body').height() +"px"); } // Walk the body and all children on a 2 second delay. setTimeout(function() { console.log('Running Conversion'); fixBody() walk('body'); console.log('Conversion Finished'); }, 2000); 

Let me know if this is not what you wanted, or if you run into a problem!

For all questions

What are common errors that can occur?

The most common error will be the wrong conversion. Something will go wrong in the conversion, and as a result, the DOM will not look like an input.

There are probably tags that don't have width or height, like svg: g. Then there are definitions of non-standard size, for example, in SVG: a circle or attributes that you do not think about at first, for example svg: text dy.

There are tags that do not have a width / height element. However, using jQuery should give us a little more reliability in this section. I have not worked with jQuery and SVG a lot, but there is a support plugin available if we encounter any problems with more obscure elements.

Is there a way to get a reasonable assumption about a performance hit due to massive remelts?

I believe this code will work in O(m * n) Where m = delay for a single reflow and n = number of nodes . Reflows should have a fairly consistent change because we convert the largest elements to the smallest. BUT I may be mistaken in this last statement.

What else?

Success!

+1
source

Source: https://habr.com/ru/post/949582/


All Articles