How to refresh html element after modifying element - html

I have been trying to print after modifying html element but element has not been changed. (Angular2)
This source code is simplified one.
<div *ngIf="displayType === 'screen'">
<div>This is Screen</div>
</div>
<div *ngIf="displayType === 'print'">
<div>This is Print</div>
</div>
And when click a button the following event.
displayType: string = 'screen'; // default
OnPrint() {
this.displayType = 'print';
let tmp = document.createElement('div');
let el = this.elementRef.nativeElement.cloneNode(true);
tmp.appendChild(el);
let content = tmp.innerHTML;
let frame1 = document.createElement('iframe');
document.body.appendChild(frame1);
let frameDoc = frame1.contentWindow;
frameDoc.document.open();
frameDoc.document.write('<html><body>'+content+'</body></html>');
frameDoc.document.close();
setTimeout(function () {
window.frames["frame1"].focus();
window.frames["frame1"].print();
document.body.removeChild(frame1);
}, 500);
}
But contents are elements before modifying.
How can I refresh elements?

To refresh the elements you need to trigger the change detection.
Here is a very good post about change detection in Angular 2: http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html
I've created a plunker here to illustrate it:
http://plnkr.co/edit/0ZwkaQ776mKpLYZJogYx?p=preview
<button (click)="OnPrint()">OnPrint</button>
Also you should not modify the DOM elements directly, you should create a component and use a structural directive to display it (here the component has a selector "my-print"):
<my-print *ngIf="isPrint" [iframeSrc]="..."></my-print>

Related

How to display current time inside a div using JQuery

I need to display the current time when I click the button each time, I've achieved the DOM of creating new divs when clicked on button, but I'm not getting the time inside span.
HTML
<button onClick="createDiv()">Record Time</button>
<div id="getText" style="display: none;">
<span id="showTime"></span>
</div>
Script
function createDiv()
{
let div = document.createElement('div');
document.body.appendChild(div);
var now = moment().format("h:mm:ss A");
$('#showTime').append(now);
}
As the code above, current time should be displayed on each new div.
I believe you are using jquery in your script, if so simply replace the
line: $('#showTime').append(now);
with: $('#showTime').text(now);
with vanilla JS you can also do this:
function createDiv(){
let div = document.createElement('div');
document.body.appendChild(div);
var now = moment().format("h:mm:ss A");
let timeNow = document.createTextNode(now);
$('#showTime').append(timeNow);
}

Replace element with razor textarea

I want to take a tag and replace with a #Html.textarea() razor html helper but it doesn't look as if JQuery can replace DOM elements with html helpers. How do I go about this?
using(#Html.BeginForm())
{
<a id="clickme">Edit</a>
<div>#Model.username</div>
}
How can I replace this div with #Html.Textarea ? JQuery could do it with div and input tags.
jQuery cannot replace a tag with #Html.TextArea() !
The TextArea helper method is a C# method, which gets executed when razor tries to render the view. This happens in your web server. jQuery is a client side library and anything you do with jQuery happens at client side, in your browser.
But all these helper methods ultimately generate some HTML for DOM elements. That means, you can use jQuery to manipulate visibility of that.
If you are trying to do something like an inline edit, you can use a script like this , to start with
First, render the text area along with your label div, but have it hidden initially. Also wrap the label,edit link and the hidden input inside a container div which we can use later to help with our jQuery selectors.
#using (#Html.BeginForm())
{
<div class="edit-item">
Edit
<div class="edit-label">#Model.FirstName</div>
#Html.TextAreaFor(a => a.FirstName,
new { style = "display:none;", #class = "edit-text" })
</div>
<div class="edit-item">
Edit
<div class="edit-label">#Model.UserName</div>
#Html.TextAreaFor(a => a.UserName,
new { style = "display:none;", #class = "edit-text" })
</div>
}
Now when the user clicks edit, you have to toggle the visibility of the label and hidden input and update the value of label after user done editing the value in the input element.
$(function () {
$("a[data-mode]").click(function (e) {
e.preventDefault();
var _this = $(this);
var c = _this.closest(".edit-item");
c.find(".edit-text").toggle();
c.find(".edit-label").toggle();
if (_this.attr("data-mode") === 'label') {
_this.attr("data-mode", 'edit');
_this.text("done");
} else if (_this.data("mode") === 'edit') {
c.find(".edit-label").text(c.find(".edit-text").val());
_this.text("edit");
_this.attr("data-mode", 'label');
}
});
});
This is a head start. You can optimize this code as needed.
Here is a working jsfiddle for your reference

Accessing the text of a class that contains other elements using Cheerio

I only want to access h1's text (H1 title is here in this case), but it prints everything. I tried adding .remove('.small-title') before text(), but it didn't work.
<div class="modal-know>
<h1>
H1 title is here
<div class="small-title">
Click
Click 2
</div>
</h1>
</div>
Node.js code
var newsTitle = $2('.modal-know h1').text(); // prints out everything
console.log(newsTitle);
have a look at cheerio docs: text()
it says
including their descendants
That is the same behaviour that jQuery .text()
So maybe this answer could help you :jQuery: using .text() to retrieve only text not nested in child tags
Here you have the code I tested:
let newsTitle = $('.modal-know h1').contents()[0].nodeValue;
// solution 2:
// .clone() //clone the element
// .children() //select all the children
// .remove() //remove all the children
// .end() //again go back to selected element
// .text(); // prints out everything
//solution 3:
// .contents().filter(function(){
// return this.nodeType == 3;
// })[0].nodeValue;
console.log(newsTitle);
*in your code sample ther is a missing " in the div modal-know class
<div class="modal-know> -> <div class="modal-know">

Automatically open <details> element on ID call

I'm trying to automatically open a element when a containing is called by ID, for example: http://www.example.com/page.html#container. Ideally I'd like this to scroll to the point of the page where the is located (inside the summary element), and then open the details element. Obviously, the native scroll function works fine, but how can I set the details element to open?
Here's my HTML source:
<details class="concentration">
<summary>
<h4 id="sample-slug"><?php the_sub_field('emphasis_name'); ?></h4>
</summary>
<p><?php the_sub_field('emphasis_description'); ?></p>
<div class="courses"><?php the_sub_field('emphasis_course_list'); ?></div>
</details>
When example.com/page.html#sample-slug is called, how can I make the details element aware of that and add the "open" attribute? I want to make sure the content is visible when the anchor is called.
I don't think you can open <details> with CSS alone. But you can:
Get the hash with location.hash. Possibly listen to hashchange event.
Use document.getElementById to get the element.
Set its open property to true.
function openTarget() {
var hash = location.hash.substring(1);
if(hash) var details = document.getElementById(hash);
if(details && details.tagName.toLowerCase() === 'details') details.open = true;
}
window.addEventListener('hashchange', openTarget);
openTarget();
:target {
background: rgba(255, 255, 200, .7);
}
<details id="foo">Details <summary>Summary</summary></details>
<div>#foo #bar</div>
Below I provide solution that works for nested <details><summary> elements:
With help of javascript you can:
track changes of hash anchor in url
react to them by changing open attribute of details elements accordingly
Here is example that does it, code here → https://stackoverflow.com/a/55377750/544721
Pure JS solution:
function openTarget() {
const hash = location.hash.substring(1);
if (hash) {
const target = document.getElementById(hash);
if (target) {
const details = target.closest('details');
if (details)
details.open = true;
}
}
}
openTarget(); // onload
window.addEventListener('hashchange', openTarget);

How to remove a shadow root from an HTML element adorned with a Shadow DOM from a template?

I'm exploring imports, templates, shadow DOM and custom elements in Chrome Canary (33.0.1712.3). In a grid layout I have a particular content element (region of the display) that will display different web components or cloned light DOM fragments imported from files.
However, I'm unable to redisplay ordinary HTML DOM once a shadow DOM has been added because I don't know how to remove the shadow root. Once created, the shadow root remains and interferes with the rendering of ordinary DOM. (I've looked at various W3C specs such as intro to web components, shadow DOM, templates, Bidelman's articles on HTML5 Rocks, etc.) I've isolated the problem in a simple example below:
Click "show plain old div"; click "show shadowed template"; click "show plain old div". Inspect in devtools after each click. After the third click, there is no output below the buttons and in devtools I am seeing:
<div id="content">
#document-fragment
<div id="plaindiv">Plain old div</div>
</div>
What do I need to add to removeShadow() to remove the shadow root and fully reset the content element to its initial state?
removing_shadows.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<template id="shadowedTemplateComponent">
<style>
div { background: lightgray; }
#t { color: red; }
</style>
<div id="t">template</div>
<script>console.log("Activated the shadowed template component.");</script>
</template>
<template id="plainDiv">
<div id="plaindiv">Plain old div</div>
</template>
</head>
<body>
<div>
<input type="button" value="show plain old div" onclick="showPlainOldDiv()"/>
<input type="button" value="show shadowed template" onclick="showShadowTemplate()"/>
<div id="content"></div>
</div>
<script>
function removeChildren(elt) {
console.log('removing children: %s', elt);
while (elt.firstChild) {
elt.removeChild(elt.firstChild);
}
}
function removeShadow(elt) {
if (elt.shadowRoot) {
console.log('removing shadow: %s', elt);
removeChildren(elt.shadowRoot); // Leaves the shadow root property.
// elt.shadowRoot = null; doesn't work
// delete elt.shadowRoot; doesn't work
// What goes here to delete the shadow root (#document-fragment in devtools)?
}
}
function showPlainOldDiv() {
console.log('adding a plain old div');
var host = document.querySelector('#content');
removeChildren(host);
removeShadow(host);
var template = document.querySelector('#plainDiv');
host.appendChild(template.content.cloneNode(true));
}
function showShadowTemplate() {
console.log('adding shadowed template component');
var host = document.querySelector('#content');
removeChildren(host);
removeShadow(host);
var template = document.querySelector('#shadowedTemplateComponent');
var root = host.shadowRoot || host.webkitCreateShadowRoot();
root.appendChild(template.content.cloneNode(true));
}
</script>
</body>
</html>
The spec of Shadow DOM moved from v0 to v1.
One of the changes is that in v1 there is no way to create shadow root on itself and the host element may contain only one shadow root.
So it seems like the answer of replacing the shadow root with a new blank shadow root is not valid anymore.
Solution paths:
if the host element self (div in your example) has no special value beside holding that Shadow DOM, one can just replace the host element as a whole
if one still likes to preserve the host, clearing the Shadow DOM with something like e.shadowRoot.innerHTML = '' might be sufficient
You can't remove a shadow root once you add it. However, you can replace it with a newer one.
As mentioned here, http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-301/, the newest shadow root "wins" and becomes the rendered root.
You can replace your shadow root with a new shadow root that only contains the <content> pseudo-element to insert everything from the light DOM back into the shadow DOM. At that point, as far as I know it will be functionally equivalent to having no shadow DOM at all.
rmcclellan is correct that you cannot truely "remove" a ShadowRoot v2. But, you can fake it.
The OuterHTML PARTIAL Solution
elementWithShadowDOMv2.outerHTML = elementWithShadowDOMv2.outerHTML;
HOWEVER, there is a major caveat: although there is no visual change, elementWithShadowDOMv2 still refers to the destroyed element with the ShadowDOMv2 as if elementWithShadowDOMv2.parentNode.removeChild( elementWithShadowDOMv2 ) were called. This also "removes" event listeners on the element. Observe the demo below.
var addShadowHere = document.getElementById("add-shadow-here");
addShadowHere.addEventListener("mouseenter", function() {
addShadowHere.style.border = '2em solid blue';
});
addShadowHere.addEventListener("mouseleave", function() {
addShadowHere.style.border = '';
});
var shadow = addShadowHere.attachShadow({mode:"open"});
var button = shadow.appendChild(document.createElement("button"));
button.textContent = "Click Here to Destroy The ShadowDOMv2";
button.addEventListener("click", function() {
addShadowHere.outerHTML = addShadowHere.outerHTML;
update();
});
update();
function update() {
// This just displays the current parent of the addShadowHere element
document.getElementById("parent-value").value = "" + (
addShadowHere.parentNode &&
addShadowHere.parentNode.cloneNode(false).outerHTML
);
}
<div id="add-shadow-here">Text Hidden By Shadow DOM</div>
addShadowHere.parentNode => <input readonly="" id="parent-value" />
Notice how the blue border stops working after you remove the ShadowDOM. That is because the event listeners are no longer registered on the new element: the event listeners remain registered on the old element that has now been removed from the DOM.
Thus, you must refresh any references to the element and reattach any event listeners. Here is an example of how you could reobtain a reference to the new element.
function removeShadowWithCaveat(elementWithShadow) {
if (!elementWithShadow.parentNode) return elementWithShadow.cloneNode(true);
var parent = elementWithShadow.parentNode;
var prior = elementWithShadow.previousSibling;
elementWithShadow.outerHTML = elementWithShadow.outerHTML;
return prior.nextSibling || parent.firstChild;
}
If you need access to the elements which are naturally hidden by the existing shadow root and which will become exposed after the expulsion of the shadow root, then here is an alternative method that will perfectly preserve these nodes.
function removeShadowWithCaveat(elementWithShadow) {
if (!elementWithShadow.parentNode) return elementWithShadow.cloneNode(true);
var ref = elementWithShadow.cloneNode(true);
while (elementWithShadow.lastChild) ref.appendChild( elementWithShadow.lastChild );
elementWithShadow.parentNode.replaceChild(elementWithShadow, elementWithShadow);
return ref;
}
Working Solution
var createShadowProp = (
"createShadowRoot" in Element.prototype ? "createShadowRoot" : "webkitCreateShadowRoot"
);
function removeChildren(elt) {
console.log('removing children: %s', elt);
while (elt.firstChild) {
elt.removeChild(elt.firstChild);
}
}
function removeShadowWithCaveat(elementWithShadow) {
if (!elementWithShadow.parentNode) return elementWithShadow.cloneNode(true);
var ref = elementWithShadow.cloneNode(true);
while (elementWithShadow.lastChild) ref.appendChild( elementWithShadow.lastChild );
elementWithShadow.parentNode.replaceChild(elementWithShadow, elementWithShadow);
return ref;
}
function showPlainOldDiv() {
console.log('adding a plain old div');
var host = document.querySelector('#content');
removeChildren(host);
// Remove the shadow
host = removeShadowWithCaveat(host);
var template = document.querySelector('#plainDiv');
host.appendChild(template.content.cloneNode(true));
}
function showShadowTemplate() {
console.log('adding shadowed template component');
var host = document.querySelector('#content');
removeChildren(host);
// Remove the shadow
host = removeShadowWithCaveat(host);
var template = document.querySelector('#shadowedTemplateComponent');
var root = host.shadowRoot || host[createShadowProp]({
"open": true
});
root.appendChild(template.content.cloneNode(true));
}
<div>
<input type="button" value="show plain old div" onclick="showPlainOldDiv()"/>
<input type="button" value="show shadowed template" onclick="showShadowTemplate()"/>
<div id="content"></div>
</div>
<template id="shadowedTemplateComponent" style="display:none">
<style>
div { background: lightgray; }
#t { color: red; }
</style>
<div id="t">template</div>
<script>console.log("Activated the shadowed template component.");</script>
</template>
<template id="plainDiv" style="display:none">
<div id="plaindiv">Plain old div</div>
</template>
Also note the misuse of vendor prefixes (a problem that far too many developers have issues with). You are correct that, at the time that this question was asked, there was only the prefixed version of createShadowRoot (which was webkitCreateShadowRoot). Nevertheless, you must ALWAYS check to see if the unprefixed createShadowRoot version is available in case if browsers standardize the API in the future (which is now the case). It might be nice to have your code working today, but it's awesome to have your code working several years from now.
In Chrome:
Press F12, DevTool will open
Click gear icon in DevTool
Uncheck "show user agent shadow DOM" checkbox
Enjoy !