Using AJAX request, I am trying to change background image and set it's style properties. In the function which is called every few seconds through setInterval method, I define style and set states of component in the following way:
changeThings() {
let maxVal = this.props.data.length;
let ranNum = Math.floor((Math.random() * maxVal));
let imgVal = (this.props.url[ranNum])? 'url('+ this.props.url[ranNum].url+')':null;
let style = {
background:imgVal,
backgroundSize:'cover',
backgroundPosition:'center',
bacgkrouhndRepeat:'no-repeate'
};
this.setState({content:{style:style,
section:section,
title:title,
by:author,
dateInfo:updatedDate}});
}
render() {
return ({
<div>
//...other components
(this.state.content.style)?
<div id="image" style={this.state.content.style}>:null
//...other components
</div>
})
}
The very first image is displayed with every background image css properties applied. From second images however, it only changes images(background:url value) but not other background image properties such as position, repeat, size etc.
What is the reason of this problems and How can I solve it?
Here is how I fixed the problem. First I only saved URL string data to this.state.content rather than entire css style. It worked when I set the style property directly to the JSX.
Also, basically when DOM define the value for style property of background-size, it calculates the value based on the current 'snapshot' value of element height and width where the image will be display. I am currently making responsive web app so I thought the css style does not work when I changed the view.
Related
I am new to CSS/JS and I am creating an accessibility chrome extension and I need to change the background color of the page. The problem I encounter is when changing the color of pages that have videos like twitch and youtube.
function changeBKColorPredefined(background, color) {
var tags = document.querySelectorAll("*");
for (let i = 0; i < tags.length; i++) {
tags[i].style.backgroundColor = background;
tags[i].style.color = color;
}
}
This code clearly changes everything there is on the page, but I don't know how to avoid changing the background color of certain tags without having to specifying them which would of course become a really hard to maintain code.
Example output of the code showed before:
I have also tried to only change the body color by using document.body.style.backgroundColor but it does not change the color of the whole page.
Thank you for any help.
Edit: an example of what I mean can be found in this extension: https://chrome.google.com/webstore/detail/a%20-fontsize-changer/ckihgechpahhpompcinglebkgcdgpkil
Use css custom properties also known as CSS Variables. This way you can set in your CSS property value as background, and then change only said value.
Simple example would be:
:root {
/**
* This will be your default value. In this example - black.
*/
--changing-background: #000;
}
/**
* In places where you want to change backround set it as background color
*/
.classes-with-changing-background {
background-color: var(--changing-background);
}
Then in JS you do:
// This is your JS. In my example we change property value to white
document.documentElement.style.setProperty('--changing-background', '#fff');
I took JS from this answer
I have a parent div card which can display one of three child divs. I want to adjust the height of the parent div to fit the exact contents of the currently-visible child div.
To do this, I create a updateDonationCardSize function which reads the refs of all three views, and depending on which one should be currently visible, sets the parent div height to match the appropriate child div height.
function updateDonationCardSize() { // Sets the size of the card to fit contents
const height = stepOneContainerRef.current.clientHeight
setDonationCardSize(prevState => ({
...prevState,
['width']: paymentFooterRef.current.clientWidth,
['height']: height
}))
}
I call this function whenever any of the refs pointing to one of the subviews updates.
useEffect(() => {
updateDonationCardSize()
setTimeout(() => updateDonationCardSize(), 700)
}, [stepOneContainerRef, stepTwoContainerRef, stepThreeContainerRef])
Notice the silly approach above where the function is called twice, once immediately and once after a brief delay. For some reason, the first time it runs, it returns the wrong height of 830 whereas the second time it runs, it returns the correct height of 700.
What's even stranger is that if I print out height variable as well as the stepOneContainerRef object into the console next to each other, the height variable is returned as 830 while stepOneContainerRef object actually displays its current.clientWidth property as 700...
I am completely clueless and have spent hours over this already.
Edit - Providing additional information
The parent div contains the following structure:
<div id='parent div' className='overflow-hidden'>
<div id='parent of dynamic content'>
<div id='containerOne' className='relative' style={stepOneContainerStyle} />
<div id='containerTwo' className='relative' style={stepTwoContainerStyle} />
<div id='containerThree' className='relative' style={stepThreeContainerStyle} />
</div>
<div id='footer' />
</div>
and the stepXContainerStyle is a dictionary which sets position: top and x to 0 if it the particular div should be currently visible, otherwise it moves it outside of the overflow region to hide it.
The different heights are result of unfinished DOM mutations during the rendering of the components. To fix that change useEffect hook to useLayoutEffect which fires synchronously after all DOM mutations. It will allow you to call updateDonationCardSize only once.
In addition, you should wrap updateDonationCardSize with useCallback for correct hook dependency specification.
const updateDonationCardSize = useCallback(() => { // Sets the size of the card to fit contents
setDonationCardSize(prevState => ({
...prevState,
['width']: paymentFooterRef.current.clientWidth,
['height']: stepOneContainerRef.current.clientHeight
}))
}, [paymentFooterRef, stepOneContainerRef]);
useLayoutEffect(() => {
updateDonationCardSize()
}, [updateDonationCardSize])
I'd also recommand to try solve this problem with CSS rules - it looks like a CSS problem more than component rendering problem.
I have this element that looks something like this:
render() {
// figure these values out based on props and state
let left = ...
let top = ...
// the values of these are floating point or integer
let src = '/image.png'
let style={top: top, left: left}
console.log("SETTING STYLE", style)
return <img src={src} style={style}/>
}
So far so good. A stylesheet sets these images to position:absolute and that works. I can control the position of the elements with props and state and business logic.
However, the element can be dragged and moved around (using Draggabilly). I have an event listener that listens to when the element stopped being dragged around. That gives me a position (x, y). I then pass that into a function which, based on some business logic, "corrects" the numbers and updates the props and state so that the element is re-rendered.
However, sometimes, the change doesn't take effect. Why?!
As you can see I console.log what the style should be and I can clearly see that it says something like Object {top: 123.5, left: 234.5} but when I inspect the element in my devtools and look at the Rules set, the numbers are different!
E.g.
Basically, those numbers sometimes don't add up with what I set when I rendered the element in the render() method.
The answer the Draggabilly plugin sets the top and left too after it has fired the event which my React app is relying on. I.e. It does something like this:
Draggabilly.prototype._stopDragging = function(event) {
var x = event.something.x; y = event.something.y;
this.triggerEvent('dragEnd', ...)
this.element.style.left = x
this.element.style.top = y
}
So two distinct systems are trying to set the style attribute.
I have encountered a problem that other people had in this website before but none of the solutions helped me slightly.
I have a method that updates an image inside a div (read: user uploads a new image) where the image is resized to fit the set proportions (max-height and max-width are 45x45). I have to resize the div that holds the image to 2* it's dimensions such as the example below:
Original image is 180x180.
It is resized to 45x45. Div has to be 90x90.
Code is as follows:
function uploadThumbnail(id, xCoord, yCoord) {
var oFReader = new FileReader();
oFReader.readAsDataURL(document.getElementById("thumbnailLoader"+id).files[0]);
oFReader.onload = function(oFREvent) {
var mapThumbnail = updateMapThumbnail(oFREvent.target.result, id);
var mapContainer = document.getElementById("thumbnail"+id);
var informationContainer = document.getElementById("container"+id);
console.log(mapThumbnail.width);
mapContainer.removeChild(document.getElementById("mapThumbnail"+id));
informationContainer.removeChild(document.getElementById("infoThumbnail"+id));
informationContainer.insertBefore(updateInfoThumbnail(oFREvent.target.result, id), informationContainer.firstChild);
mapContainer.appendChild(mapThumbnail);
};
};
function updateMapThumbnail(result, id){
var newThumbnail = document.createElement("img");
newThumbnail.src = result;
newThumbnail.style.maxWidth = "45px";
newThumbnail.style.maxHeight = "45px";
newThumbnail.id = "mapThumbnail" + id;
return newThumbnail;
}
As you can see I added a console.log method there for test purposes. The problem I am facing is that generating mapThumbnail with set max dimensions (45x45) still has the height and width attributes set with the original image size. I tried reading image.height/width and image.style.height/width as well as naturalHeight/width and clientHeight/width.
None of these solutions return the height and width after resizing.
Thanks for your time.
Also, please refrain from offering solutions that require JavaScript libraries.
Edit: forgot to mention that the image placed inside the div is re-sized to the dimensions that I do want it to be. It's just the attributes that seem to be wrong.
There are four different and undependant widths on an image:
1) img.naturalWidth is the width in px the original image file has. It doesn't change when you set the other widths to some value. This value is rendered when the others are not defined.
2) img.width is an attribute of img. You find it inside the html img tag and can set it with img.setAttribute('width', 'value'). It doesn't change when the others are set to some value. This value is rendered when 3) is not defined and 4) is >= img.width or not defined.
3) img.style.width is a css-property of the images style. You can set it in your css or into the style-attribute in the img tag with img.style.width = 'value';. It doesn't change when the others are set to some value. This value is rendered when it is <= 4) or 4) is not defined.
4) img.style.max-width is another css-property of images style. You can set it in your css or into the style-attribute in the img tag with img.style.maxWidth = 'value';. It doesn't change when the others are set to some value. This is rendered when 2) or 3) are not defined or have values > 4).
So you have to decide by yourself which value you want to receive or set.
Its the same with height, there are also four heights.
EDIT According to your comment:
Inside your function updateMapThumbnail you create a new img. A newly created img has no .width unless you define it with newThumbnail.setAttribute('width', 'value). Same with img.style.width: unless you set it explicitely somewhere it's simply not there.
Did you try:
console.log(getComputedStyle(mapThumbnail).width);
I'm trying to change the background image of a button when the mouse is hovered.
with the statement
function testIn ()
{
elem.style.backgroundImage = 'url("image_name_in.png")';
}
function testOut ()
{
elem.style.backgroundImage = 'url("image_name_out.png")';
}
i'm doing this with onMouseOver=testIn() and onMouseOut=testOut().
Here the problem is that, when i hover the mouse. I'm seeing the progress bar (bottom right side) is shown in firefox as if some page is getting loaded
Use :hover pseudo-class and CSS Sprites instead.
You need a few changes in order to pass your object reference:
onMouseOver="testIn(this)"
function testIn (elem)
{
elem.style.backgroundImage = 'url("image_name_in.png")';
}
BTW - convention now uses "onmouseover" (no caps)
You're getting activity in the progress bar because your onmouseover image does NOT load until you call the function.
You could use a sprite combined with :hover CSS for the effect - like #Tomasz mentions.
If you don't want to combine your default and hover image states into a sprite, you may try adding an additional container for the hover image (setting it's default CSS to display:none;) then use JS, or jQuery to swap the display states of the default and hover images on mouseover or hover.
$('myDefaultImage').hover(function() {
$(this).hide();
$('myHoverImage').show();
}, function () {
...the inverse, etc.
});
This will eliminate the progress bar issue because all of your images will be loaded together.
At the same time, this is going to bloat your page size unnecessarily.
I'd really try to go with the :hover CSS and sprite, or reevaluate the importance of what you're trying to accomplish with the image swap (is it really the best solution for your overall project?).