Get value of element once then keep that value static - javascript

I am using
function expand(btn) {
var box = btn.parentNode.parentNode,
ipsum = box.getElementsByTagName("p")[0],
textSize = window.getComputedStyle(ipsum, null).getPropertyValue('font-size'),
lineHeight = window.getComputedStyle(ipsum, null).getPropertyValue('line-height'),
boxWidth = window.getComputedStyle(box, null).getPropertyValue('width'),
initialHeight = window.getComputedStyle(box, null).getPropertyValue('height'),
numText = parseInt(textSize),
numWidth = parseInt(boxWidth),
numHeight= parseInt(initialHeight);
if(box.style.height == "150px"){
box.style.height = "40px";
ipsum.style = "display:none";
}
else{
box.style.height = "150px";
ipsum.style = "display:inline";
}
console.log(lineHeight);
}
to get the initial height value of an element the only problem is that the element height changes frequently, but the first value obtained is always correct how can i get the initial value and keep it static?
how do i only store the value in the variable once, i need it in a variable to do calculations but as the value keeps changing i am getting the wrong number outputs.

You can refactor the function to store the initialHeight in a "private" variable the first time it's run:
var expand = (function() {
var initialHeight;
// Return a function that holds initialHeight in a closure
return function (btn) {
// Get box before setting/getting initialHeight
var box = btn.parentNode.parentNode;
// Set initialHeight only if undefined
initialHeight = initialHeight || window.getComputedStyle(box, null).getPropertyValue('height');
// Do other variables
var ipsum = box.getElementsByTagName("p")[0],
textSize = window.getComputedStyle(ipsum, null).getPropertyValue('font-size'),
lineHeight = window.getComputedStyle(ipsum, null).getPropertyValue('line-height'),
boxWidth = window.getComputedStyle(box, null).getPropertyValue('width'),
numText = parseInt(textSize),
numWidth = parseInt(boxWidth),
numHeight= parseInt(initialHeight);
if(box.style.height == "150px"){
box.style.height = "40px";
ipsum.style.display = "none";
} else {
box.style.height = "150px";
// If ipsum is a P, probably better to use "" (empty string) here
// so it returns to its default or inherited value
// ipsum.style.display = "inline";
ipsum.style.display = "";
}
console.log(lineHeight);
}
}());
The above is a proper refactoring, tested with the following markup:
<style type="text/css">
#box {border: 1px solid blue;}
#notBox {border: 1px solid red;}
#ipsum {border: 1px solid yellow;}
</style>
<div id="box">box
<div id="notBox">notBox
<input type="button" onclick="expand(this)" value="Expand…">
<p id="ipsum">ipsum</p>
</div>
</div>

Can you set it as an attribute? Something like:
// set the value once some place
box.setAttribute('data-init-height', window.getComputedStyle(…)… );
// when setting the initial height, check for the attribute first
initialHeight = box.getAttribute('data-init-height') || window.getComputedStyle(…)…;
Follow-up:
see fiddle

Each time you execute the function expand, you got a new value for initialHeight.
All that you need is to record it in a closure, with a hash if you're having more than 1 btn to handle, valued with arrays if you'd like to record multi heights for each btn. Just like this:
// predefine the function expand for furthre usage.
var expand;
(function() {
/*
having arrays as value, indexed like this:
{
<btn_1_id> : [<firstHeight>, <2nd Height>, ...],
<btn_2_id> : [],
...
}
*/
// Let's assume every btn is having an id. You may think another way yourself it they don't.
var initialHeightForMultiBtns = {};
expand = function(btn) {
// ...blablabla
initialHeightForMultiBtns[btn.id] = initialHeightForMultiBtns[btn.id] || [];
initialHeightForMultiBtns[btn.id].push(window.getComputedStyle(box, null).getPropertyValue('height'));
console.log(initialHeightForMultiBtns[btn.id][0]); // the real initialized height for the given btn.
// ...blablabla
}
})()
expand(btn_1); // let's expand btn_1 here.
Good luck.

Related

Force duplicate printElement to go to the bottom part of the page using window.print();

I am planning to do a duplicated printing. The top-half is for the staff's copy, and the lower-half part is for the user's copy. We will just cut the paper in half once it's printed. How do we do this? Can the window.print(); do this?
This is the script that I am using.
function PrintAppendChangeScheduleButton() {
printElement(document.getElementById("divID")); //Specify the DIV to be printed.
function printElement(elem) {
var forDOMClone = elem.cloneNode(true);
var $forSECTIONPrint = document.getElementById("forSECTIONPrint"); //For Section Specific Print
if (!$forSECTIONPrint) {
var $printSection = document.createElement("div"); //For DIV Specific Print
$forSECTIONPrint.id = "forSECTIONPrint";
document.body.appendChild($forSECTIONPrint);
} else {
$forSECTIONPrint.innerHTML = "";
$forSECTIONPrint.appendChild(forDOMClone);
window.print();
return true;
}
}
}
I tried duplicating elem.cloneNode(true);, but it does not arrange it properly.
This is what I'm working on right now.
function PrintAppendChangeScheduleButton() {
printElement(document.getElementById("divID")); //Specify the DIV to be printed.
function printElement(elem) {
var forDOMClone = elem.cloneNode(true);
var forDOMCloneCUT = elem.cloneNode(true);
var $forSECTIONPrint = document.getElementById("forSECTIONPrint"); //For Section Specific Print
if (!$forSECTIONPrint) {
var $printSection = document.createElement("div"); //For DIV Specific Print
$forSECTIONPrint.id = "forSECTIONPrint";
document.body.appendChild($forSECTIONPrint);
} else {
$forSECTIONPrint.innerHTML = "";
$forSECTIONPrint.appendChild(forDOMClone);
$forSECTIONPrint.appendChild(forDOMCloneCUT);
window.print();
return true;
}
}
}
This is the current printing status.
This is the result that I am looking for.
Is there a way for javascript to force $forSECTIONPrint.appendChild(forDOMCloneCUT); to go to the lowest part of the page?
I have hacked something together.
First, we need to know that the size of an A4 sheet is 210mm x 297mm. I got that from here.
Next, we need to convert the height (297mm) to pixels. We do that here and get 1122.5px.
Now we need to measure the height of the div you want to print twice, and see if twice the height of the div is less than the size of an A4 sheet. If yes, then we create an empty div in between the 2 clones and give it the height of whatever empty space is there after the clones.
So here's your modified code:
* {
margin: 0;
padding: 0
}
<div id='divID' style="border: 2px black solid; padding-bottom: 200px">
<h1>CONTENT FROM THIS PAGE IS FROM printElement(document.getElementById('div1'))</h1>
</div>
<div id='forSECTIONPrint'></div>
<script type="text/javascript">
window.onload = PrintAppendChangeScheduleButton;
function PrintAppendChangeScheduleButton() {
printElement(document.getElementById("divID")); //Specify the DIV to be printed.
function printElement(elem) {
var forDOMClone = elem.cloneNode(true);
var forDOMCloneCUT = elem.cloneNode(true);
var $forSECTIONPrint = document.getElementById("forSECTIONPrint"); //For Section Specific Print
if (!$forSECTIONPrint) {
var $printSection = document.createElement("div"); //For DIV Specific Print
$forSECTIONPrint.id = "forSECTIONPrint";
document.body.appendChild($forSECTIONPrint);
} else {
$forSECTIONPrint.innerHTML = "";
var elemHeight = elem.offsetHeight;
elem.style.display = 'none';
var emptySpace = document.createElement('div');
$forSECTIONPrint.appendChild(forDOMClone);
$forSECTIONPrint.appendChild(emptySpace);
//if there's any empty space at the bottom of the page, then set the height of the
//empty div in between the clones with that space height
if (1122.5 - (elemHeight * 2) > 0){
setTimeout(function(){
emptySpace.style.height = 1122.5 - (elemHeight * 2) + 'px';
window.print();
},100);
}
//if there's no empty space, just print right away
else {
window.print();
}
$forSECTIONPrint.appendChild(forDOMCloneCUT);
return true;
}
}
}
</script>

Counter is not changing color according to the function

the counter needs to turn red when i reach 10 characters. When i reach 10 characters, i have to press any button again in order for the counter to turn red. When i delete the characters, the counter remains red instead of going back to black.
<script>
function ceScriu(){
var numeInput = document.querySelector("[name='nume']");
var nume = numeInput.value;
var nu = nume.toString().toLowerCase();
var divInput = document.querySelector("[name='divul']");
var div = nu.length;
document.querySelector("[name='divul']").innerHTML = div;
numeInput.onkeypress = function(){
if(div === 10){
event.preventDefault();
divInput.classList.add("counter");
}else{
divInput.classList.remove("counter");
}
}
}
</script>
I want the counter to turn red when i enter the 10th character and i want the counter to turn back to black when i start deleting characters after i reached the 10th.
1.) By declaring the variables before the onkeypress, you're passing the original values. It might be better to check the length/value again inside the keypress function.
2.) Onkeypress does not work with backspaces. You should use "onkeyup" in order to detect the backspace change.
3.) If you want to detect 10 or more characters (and keep the box red until the length is less than 10), you should use div >= 10 as a comparison (more than or equal to 10)
`
function ceScriu(){
// *1
var numeInput = document.querySelector("[name='nume']");
var nume = numeInput.value;
var nu = nume.toString().toLowerCase();
var divInput = document.querySelector("[name='divul']");
var div = nu.length;
document.querySelector("[name='divul']").innerHTML = div;
// *2 numeInput.onkeypress = function(){
/* New event check*/
numeInput.onkeyup = function(){
/* End new event check*/
/* New length Check */
let nume = numeInput.value;
let nu = nume.toString().toLowerCase();
let div = nu.length;
/* End new length check */
// *3 if(div === 10){
/* New comparison */
if(div >= 10){
/* End new comparison */
event.preventDefault();
divInput.classList.add("counter");
}else{
divInput.classList.remove("counter");
}
}
}
`
Hope that helps!
onkeypress is not suitable in this case. You can use either onkeyup or oninput event. To restrict the inputs up to certain length you can use maxlength attribute.
Try the following way:
function ceScriu(el){
var divInput = document.querySelector("[name='divul']");
var div = el.value.trim().length;
document.querySelector("[name='divul']").textContent = div;
if(div === 10){
divInput.classList.add("counter");
}
else{
divInput.classList.remove("counter");
}
}
.counter{
color:red;
}
<input name="nume" oninput="ceScriu(this)" maxlength="10"/>
<div name="divul"></div>
You need define a style ".counter" first and put the script and style inside the tags, your code works when you try to press 11 times, if i put 1234567890 and try to put another, the style changes and set the red color
.counter{
color:red;
width:100px;
}
<script>
function ceScriu(){
var numeInput = document.querySelector("[name='nume']");
var nume = numeInput.value;
var nu = nume.toString().toLowerCase();
var divInput = document.querySelector("[name='divul']");
var div = nu.length;
document.querySelector("[name='divul']").innerHTML = div;
numeInput.onkeypress = function(){
if(div === 10){
document.querySelector("[name='divul']").classList.add("counter");
event.preventDefault();
}else{
divInput.classList.remove("counter");
}
}
}
</script>
</head>
Thank you for all your answers. Some of them were of help and led me to this result.
It seems that all i needed to do was just add 2 more IF statements.
I'm adding the code here.
function ceScriu(){
var numeInput = document.querySelector("[name='nume']");
var nume = numeInput.value;
var nu = nume.toString().toLowerCase();
var divInput = document.querySelector("[name='divul']");
var div = nu.length;
document.querySelector("[name='divul']").innerHTML = div;
numeInput.onkeypress = function(){
if(div >9){
event.preventDefault();
}else{
divInput.classList.remove("counter");
}
}
if(div<10){
divInput.classList.remove("counter");
}
if(div>9){
document.querySelector("[name='divul']").classList.add("counter");
}
}

setInterval and clearInterval not working

I am trying to make a dynamic progress bar which starts from 1% width and goes to 100% width;
Here is the function
const dynamicBar = (id)=>{
let bar = document.getElementById(id);
let width = 1;
let id= setInterval(frame,10);
function frame(){
if(width>=100){
clearInterval(id);
}
else{
bar.style.width = width + "%";
width++;
}
}
};
And the html part:
<div class="w3-light-grey">
<div class="w3-red" style="width:20%;height:20px;" id="bar1">
</div>
</div>
<button onClick="dynamicBar('bar1')" class="w3-button">Click Me</button>
but the function does not work. It works if i use var instead of let but i want to know how to do this using let keyword.
let doesn't allow you to define something twice, so you need to use a different name. You had id for both your parameter and in line 5, which isn't allowed with let.
For example,
let var1 = true;
let var1 = false;
would throw an error, since it defines var1 twice. However, you can change the value.
let var1 = true;
var1 = false;
The code block above would work just fine. let is used so that you don't accidentally override a variable in a big program.
const dynamicBar = (idName) => {
let bar = document.getElementById(idName);
let width = 1;
let id = setInterval(frame, 10);
function frame() {
if (width >= 100) {
clearInterval(id);
} else {
bar.style.width = width + "%";
width++;
}
}
};
<div class="w3-light-grey">
<div class="w3-red" style="width:20%;height:20px;background-color:red;" id="bar1">
</div>
</div>
<button onClick="dynamicBar('bar1')" class="w3-button">Click Me</button>
when you are using var, your first Id is been overridden by your second id assignment, so it will not throw an error. but when you use let, it tends to keep the code integrity and says no.
as an example
var foo = "something"
var foo = "another thing"
console.log(foo) //another thing
but
let foo = "something"
let foo = "another thing"
give you this, "Identifier 'foo' has already been declared"
one more thing, is that use const for constant values.
instead of this
let id = setInterval(frame, 10);
use
const interval = = setInterval(frame, 10);
make sure you clear interval inside fame
clearInterval(interval)
and your code will start working

Javascript run a function at the same time with different vars

Sorry about the confusing title, I'll explain better.
I have a 20x20 grid of div's, so its 400 of them each with an id, going from 0 to 399.
Each div is given one of three random values - red, green or blue - and when a div is clicked, a function is run to check if the div to the left, right, over and under are of the same value, if it is of the same value it will be simulated a click and the same function will run again.
The problem, is that the function sets vars, so if it finds that the div below has the same value, it will overwrite the vars set by the first click, hence never click any of the others.
JSfiddle - http://jsfiddle.net/5e52s/
Here is what I've got:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>untiteled</title>
<style>
body {
width: 420px;
}
.box {
width: 19px;
height: 19px;
border: 1px solid #fafafa;
float: left;
}
.box:hover {
border: 1px solid #333;
}
.clicked {
background: #bada55 !important;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$().ready(function(){
var colors = ['red', 'green', 'blue'];
var i = 0;
while(i<400){
var color = colors[Math.floor(Math.random() * colors.length)];
$('.test').append('<div class="box" id="'+i+'" value="'+color+'" style="background:'+color+';">'+i+'</div>');
i++;
}
$('.box').click(function(){
var t = $(this);
t.addClass('clicked');
id = t.attr('id');
val = t.attr('value');
//Set color
up = parseInt(id) - 20;
right = parseInt(id) + 1;
down = parseInt(id) + 20;
left = parseInt(id) - 1;
clickup = false;
clickdown = false;
if($('#'+down).attr('value') === val){
clickdown = true;
}
if(up > -1 && ($('#'+up).attr('value') === val)){
clickup = true;
}
if(clickdown == true){
$('#'+down).click();
}
if(clickup == true){
$('#'+up).click();
}
});
});
</script>
</head>
<body>
<div class="test">
</div>
</body>
I think the biggest root cause of your problem is you don't check if it already has class 'clicked' or not. That could make the infinite recursive. For example, if you click on the div#2 then the div#1 receives a simulated click, and div#2 receives a simulated click from div#1.
$('.box').click(function(){
var t = $(this);
if(t.hasClass('clicked')) {
return;
}
t.addClass('clicked');
var id = t.attr('id');
var val = t.attr('value');
//Set color
var up = parseInt(id) - 20;
var right = (id%20 != 19) ? ((0|id) + 1) : 'nothing' ;
var down = parseInt(id) + 20;
var left = (id%20 != 0) ? ((0|id) - 1) : 'nothing';
console.log(up, right, down, left);
if($('#'+down).attr('value') === val) {
$('#'+down).click();
}
if($('#'+right).attr('value') === val) {
$('#'+right).click();
}
if($('#'+up).attr('value') === val) {
$('#'+up).click();
}
if($('#'+left).attr('value') === val) {
$('#'+left).click();
}
});
You can schedule the clicks onto the event loop instead of calling them directly, eg:
if(clickdown == true){
setTimeout(function () {
$('#'+down).click();
});
}
I don't think that's your root cause though, it's probably a combination of global vars and scope issues. Try reformatting as such:
$('.box').click(function (event){
var t = $(this), id, val, up, right, down, left, clickup, clickdown;
//...
Your variables id and val are not in a var statement, thus are implicitly created as members of the window object instead of being scoped to the local function. Change the semicolon on the line before each to a comma so that they become part of the var statement, and your code should begin working.

elementFromPoint() full document alternative

I am trying to recover a value from a cookie, which is somewhere on the Y-axis where the user clicked. I then want to find the parent <h2> from that click (if it helps, all the <h2>s are the first child of a <div class="_bdnable_">). Here is what I have so far:
var bookmarkLocation;
function getBookmarkPos() {
if ($.cookie("bookmark-position") !== null) {
$(".bdnable").each(function(i) {
var scrollTopTop = $(this).offset.top;
var scrollTopBottom = $(this).offset.top + $(this).height();
// var screenWidth = parseInt(screen.width/2);
// alert(screenWidth);
// var bookmarkPosition = parseInt($.cookie("bookmark-position"));
// alert(bookmarkPosition);
// var query = document.elementFromPoint(screenWidth, 50).nodeName;
// alert(query);
if ($.cookie("bookmark-position")>=scrollTopTop && $.cookie("bookmark-position")<=scrollTopBottom) {
bookmarkLocation = $(this).closest("div").children(":nth-child(1)").text();
}
});
if (bookmarkLocation == null) {
bookmarkLocation = "Unknown";
}
} else {
bookmarkLocation = "No bookmark set";
}
$("#bookmarklocationspan").html(bookmarkLocation);
}
In the commented out section is where I tried to use getElementFromPoint and then realized that it only checks the visible area. Not good, because the scrollable Y-axis on the page is 1000s of pixels tall.
Any ideas are greatly appreciated!!!
If you already have the y-coordinate of the click from the cookie, why not simply compare all H2's y-position and pick the one which is the next "higher" one? Your approach looks like it's compares whether the user has clicked directly on the H2 instead of the article/button below it?
Just an idea - don't rate it's style, think its prettey messy:
var $myH2 = $('h2');
var clickY = <COOKIE_VALUE>;
var currentY = 0;
var foundH2ID = '';
for (var i = 0; i < $myH2.length; i++) {
var h2Y = $($myH2[i]).position().top;
if (h2Y <= clickY && h2Y > currentY) {
currentY = h2Y;
foundH2ID = $myH2[i].id;
}
}
Or maybe I got you wrong?

Categories