State of element before and after editing (using contentEditable) - javascript

I would like to capture how the elements within a div change after a user edits it (as the content is contentEditable), and so have a page like the following:
before_html = $("#example_div").children();
$("#differences_button").on("click",function(){
after_html = $("#example_div").children();
console.dir(before_html[0].innerText);
console.dir(after_html[0].innerText);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="example_div" contentEditable><span id='example_span'>How it starts</span></div>
<button type="button" id="differences_button">Show differences</button>
However, as the console.dirs show, the "before_html" doesn't store the original structure of the element, but will show you the latest structure of it when running it again.
Is there a way to store the structure before the sort of changes shown in this example?
I've tried JSON.parse(JSON.stringify(before_html)) to store something that won't update, which often works when trying to store a javascript variable you don't want later update, but this fails to store the content when applied here.

The problem is that you are accessing before_html[0].innerText and after_html[0].innerText after the click. So both of them are evaluated after all changes are made.
Instead, you can save before_html (prior to attaching the event handler), and have it contain the innerHtml or innerText, and then compare with the new value during the click handler.
before_text = $("#example_div").text();
$("#differences_button").on("click",function(){
after_text = $("#example_div").text();
console.dir(before_text);
console.dir(after_text);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="example_div" contentEditable><span id='example_span'>How it starts</span></div>
<button type="button" id="differences_button">Show differences</button>
Note that I have changed the variable name from before_html to before_text as it doesn't contain HTML. You can have it contain HTML by calling before_text = $("#example_div").html(); if you prefer that.

Related

passing values to a function does not work when inside for loop

I'm mapping currencies from a json file and i render the mapped currencies to a component. I have a .php file like this
<div class="currency-switch-container" id="currency_container">
<span style="font-size:12px;font-weight:bold">All currencies</span>
<div id="currency-map" style="margin-top:15px"></div>
</div>
I refer the div in the above component in my js file as follows
let currencyMap = jQuery("#currency-map");
And when my jQuery document is ready i'm doing the following
jQuery(document).ready(function($) {
$.getJSON('wp-content/themes/mundana/currency/currency.json', function(data) {
for(let c in data){
currencyMap.append(`<span onclick="onCurrencyClick(${data[c].abbreviation})"
class="currency-item">
<span>
${data[c].symbol}
</span>
<span>
${data[c].currency}
</span>
</span>`)
}
});
}
and my function is like this
function onCurrencyClick(val){
console.log("val",val);
setCookie("booking_currency", val, 14);
}
Here the function does not work. But if i do not pass anything to the function it seems to work as i can see the log in the terminal.
Hi your expression ${data[c].abbreviation} will put the value into function string without string quotes i.e. the resultant would be onCurrencyClick(abbreviation) while it should be onCurrencyClick('abbreviation').
please use onclick="onCurrencyClick('${data[c].abbreviation}')" instead.
Instead of using the inline onclick, use event delegation. This means that you have a single event listener that handles all the events from the children and grandchildren. The modification is a very minor one seeing the example here below.
A reason for doing this is that you keep your JavaScript inside your JS file. Like now, you encounter a JS error and have to look for it in your HTML. That can get very confusing. Also however inline onclick listeners are valid, they are outdated and should be avoided unless there is absolutely no other way. Compare it with using !important in CSS, same goes for that.
function onCurrencyClick(event){
var val = $(this).val();
setCookie("booking_currency", val, 14);
}
currencyMap.on('click', '.currency-item', onCurrencyClick);
This example takes the val that you try to insert from the value attribute from the clicked .current-item. <span> elements don't have such an attribute, but a <button> does and is a much more suitable element for it expects to be interacted with. It is generally a good practice to use clickable elements for purposes such as clicking.
In the example below you see the button being used and the abbreviation value being output in the value attribute of the <button> element and can be read from the onCurrencyClick function.
jQuery(document).ready(function($) {
$.getJSON('wp-content/themes/mundana/currency/currency.json', function(data) {
for(let c in data){
currencyMap.append(`
<button value="${data[c].abbreviation}" class="currency-item">
<span>
${data[c].symbol}
</span>
<span>
${data[c].currency}
</span>
</button>
`)
}
});
onclick will not work for a dynamically added div tag
Yo should follow jQuery on event
Refer: jQuery on
Stackoverflow Refer: Dynamic HTML Elements

Swapping BODY content while keeping state

Dynamically swapping BODY content using jQuery html function works as expected with 'static' content.
But if forms are being used, current state of inputs is lost.
The jQuery detach function, which should keep page state, seems to be blanking out the whole page.
The following code is the initial idea using jQuery html but of course the text input value will always empty.
function swap1( ) {
$("body").html('<button onclick="swap2();">SWAP2</button><input type="text" placeholder="swap2"/>');
}
function swap2( ) {
$("body").html('<button onclick="swap1();">SWAP1</button><input type="text" placeholder="swap1"/>');
}
With not knowing what form inputs there are, how would one swap in and out these forms in the BODY and keep track of the form states?
Demo of two text inputs which should keep state when they come back into the BODY:
https://jsfiddle.net/g7hksfne/3/
Edit: missunderstood use case. This covers saving the state while manipulating the DOM, instead of switching between different states.
Instead of replacing the <body>'s content by serializing and parsing HTML, learn how to use the DOM. Only replace the parts of the DOM you actually change, so the rest of it can keep its state (which is stored in the DOM).
In your case: you might want to use something like this:
function swap1( ) {
document.querySelector("button").onclick = swap2;
document.querySelector("button").textContent = "SWAP2";
document.querySelector("input").placeholder = "swap2";
}
function swap2( ) {
document.querySelector("button").onclick = swap1;
document.querySelector("button").textContent = "SWAP1";
document.querySelector("input").placeholder = "swap1";
}
<button onclick="swap1();">SWAP1</button><input type="text" placeholder="swap1"/>
(This is not optimized and should only serve as an example.)
Put the content you want to save in a node below <body>, like a simple ยด` if you don't already have a container. When you want to save and replace the container, use something like:
var saved_container = document.body.removeChild(document.querySelector("#app_container");
// or document.getElementById/getElementsByClassName, depends on container
The element is now detached and you can add your secondary to document.body. When you want to get back, save the secondary content (without overwriting the first container of course), then reattach the primary content it with:
document.body.appendChild(savedContainer);

How to display and store button values?

very new to coding here, apologies for the basic question. trying to complete the odin project's build-a-calculator challenge (http://www.theodinproject.com/javascript-and-jquery/on-screen-calculator)
and struggling to make numbers appear after they are clicked. Also, how would I store the value in a variable or array to then be used in a calculation later?
Here's the excerpt of my JS:
$(".numbers").on("click", function() {
$+(this).text()
;});
And my HTML (note I'm using jsfiddle, hence the lack of html opening and closing tags etc:
<script src="jquery-1.11.3.min.js"></script>
<div class="numbers">
<button type="button">0</button>
<button type="button">1</button>
<button type="button">2</button>
<button type="button">3</button>
<button type="button">4</button>
<button type="button">5</button>
<button type="button">6</button>
<button type="button">7</button>
<button type="button">8</button>
<button type="button">9</button>
</div>
<div class = "operators">
<button type="button">+</button>
<button type="button">-</button>
<button type="button">*</button>
<button type="button">/</button>
<button type="button">=</button>
<button type="button">clear</button>
</div>
To store the value of the buttons in a variable you could do this.
$('button').on('click', function(){
var i = $(this).text();
console.log(i); // print the value of i in the console
});
Once you have the value you'll need to be able to put the value of each button clicked in order on the "display" of the calculator like so.
HTML
<div class="display"></div>
JavaScript
$('button').on('click', function(){
var i = $(this).text();
var display = $('.display');
display.text( display.text() + i );
});
Hopefully that helps point you in the right direction.
I am not sure how you want to display your numbers. Are you using a TextArea?
For storing values, inside your function do something like
var num=$+(this).text()
Other than that, you need to be more specific.
The following jsfiddle demonstrated how to do what you want.
// array is defined outside the click handler
var clickedArray = [];
$('button').on('click',function() {
// get the button that was clicked
var buttonClicked = $(this).html();
console.log(buttonClicked);
// store it as the last element in the array
clickedArray.push(buttonClicked);
console.log(clickedArray);
// and output it to the screen
$('.js-calc-out').append(buttonClicked);
});
Points to note:
The array is defined outside the click event handler so it can be used without being reset each time a click event is fired. This array will exist until the page is refreshed or it is purposely unset and you can access it as you need it.
The html() function retrieves the contents of any given HTML element, in this case it's the clicked button, which is retrieved using $(this) (any element an event is fired on is retrieved using this which is then converted to a jquery object using the $() function).
The push() function is used to append the latest clicked element to the end of the array mentioned in point 1.
.js-calc-out is an HTML element to which we append() the latest click to, meaning the sequence of clicks are output.
The console.log declarations output some stuff into the inspector which should help you see the process develop.
PS. this is a simplified solution that takes into account your current position on the learning curve; ideally you'd want to use objects to encapsulate data and functionality in javascript and keep out of the global namespace.

jQuery "add" Only Evaluated When "appendTo" Called

this has been driving me crazy since yesterday afternoon. I am trying to concatenate two bodies of selected HTML using jQuery's "add" method. I am obviously missing something fundamental. Here's some sample code that illustrated the problem:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
<p id="para1">This is a test.</p>
<p id="para2">This is also a test.</p>
<script>
var para1 = $("#para1").clone();
var para2 = $("#para2").clone();
var para3 = para1.add(para2);
alert("Joined para: " + para3.html());
para3.appendTo('body');
</script>
</body>
</html>
I need to do some more manipulation to "para3" before the append, but the alert above displays only the contents of "para1." However, the "appendTo appends the correct, "added" content of para1 and para2 (which subsequently appears on the page).
Any ideas what's going on here?
As per the $.add,
Create a new jQuery object with elements added to the set of matched elements.
Thus, after the add, $para3 represents a jQuery result set of two elements ~> [$para1, $para2]. Then, per $.html,
Get the HTML contents of the first element in the set of matched elements or set the HTML contents of every matched element.
So the HTML content of the first item in the jQuery result ($para1) is returned and subsequent elements (including $para2) are ignored. This behavior is consistent across jQuery "value reading" functions.
Reading $.appendTo will explain how it works differently from $.html.
A simple map and array-concat can be used to get the HTML of "all items in the result set":
$.map($para3, function (e) { return $(e).html() }).join("")
Array.prototype.map.call($para3, function (e) { return $(e).html() }).join("")
Or in this case, just:
$para1.html() + $para2.html()
Another approach would be to get the inner HTML of a parent Element, after the children have been added.

Listen for changes in DOM

I am working on a reflection of part of my website. This is the relevant HTML:
<div id = "original">
<img src = "picture.png" alt = "A picture" id = "picture">
<p id = "text">Some text</p>
</div>
<div id = "reflection"></div>
My Idea is copying the content of div#original to div#reflection like this:
<script>
function reflect()
{
var original = document.getElementById("original");
var reflection = document.getElementById("reflection");
reflection.innerHTML = original.innerHTML;
}
</script>
I am aware, that this will make the HTML invalid, in the whole project I iterate through the elements copied and set the IDs to not have that side effect. I just thought that this would be unnecessary to include here.
The problem I have is that the HTML I want to reflect is dynamic, so it may change. Then the reflection is wrong. I have searched for an event handler for this, but haven't found one. The only one near I found was onchange, but apparently this listens to changes in the attributes, not the child elements. Is there another one for this, or did I just use onchange wrong?
Thank you for your help!
GeF
I am aware that I could add onchange to every element, but this seemed not good style to me.
The simplest thing you can do is add a callback to reflect() every time you change the contents of the #original.
If this is not an option, you can look into a MutationObserver (documetation), as suggested here: Is there a JavaScript/jQuery DOM change listener?

Categories

Resources