DevTools in newer browsers display the light and shadow DOM. But what seems to be missing is a "Copy flatten DOM" that captures the #shadow-root along with the root elements.
I've sorted out how to, for example, display the shadow DOM in DevTools, but Copy Element only copies the light DOM.
Any tips?
1) Copy the shadowRoot.innerHTML in a <template> element.
2) For each <slot> element, get the distributed content with the assignedNodes() method and replace the <slot> (with replaceChild()) by it.
var sh = host.attachShadow({mode:'open'})
sh.innerHTML = `<h4>Shadow DOM</h4> Content: <div><slot name="node1"></slot></div>`
function slotContent( slot )
{
var frag = document.createDocumentFragment()
var nodes = slot.assignedNodes( { flatten:true } )
for ( let node of nodes )
{
frag.appendChild( node.cloneNode( true ) )
}
return frag
}
function flatten()
{
var template = document.createElement( 'template' )
template.innerHTML = host.shadowRoot.innerHTML
var slots = Array.from( host.shadowRoot.querySelectorAll( 'slot' ) )
var slots2 = Array.from( template.content.querySelectorAll( 'slot' ) )
for ( var i in slots )
{
var frag = slotContent( slots[i] )
slots2[i].parentNode.replaceChild( frag, slots2[i] )
}
console.log( template.innerHTML )
}
<div id=host>
<span slot=node1>Distributed Node</span>
</div>
<button onclick=flatten()>Flatten</button>
Related
Attempting to populate a select form using an object I'm passing to my component as a prop. The object looks like this: {24: {14: 64.99, 20: 89.99, 26: 114.99}, 30: {14: 74.99, 20: 99.99, 26: 124.99} and I'm attempting to isolate the 24 and the 30 as values in my form. Here's the relevant code:
import React, { Component } from 'react';
import { Row, Input } from 'react-materialize';
class HeightPicker extends Component {
constructor(props){
super(props);
this.state = {
height: '',
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({height: e.target.value});
}
displayHeights(){
let prices = this.props.prices;
let height;
let heights = [];
console.log(prices);
for (height in prices) {
heights.push(height)
};
console.log(heights);
heights.forEach(function(h) {
return(<option value={h}>{h}</option>);
});
}
render() {
return(
<div>
<Row>
<Input l={12} value={this.state.height} onChange={this.handleChange} type='select' label="Height">
{this.displayHeights()}
</Input>
</Row>
</div>
)
};
};
export default HeightPicker;
As constructed above, it's returning a blank form. If I hardcode options into my render function it works, therefore I'm assuming my issue is arising through my displayHeights function. But I also was running into some issues earlier with React Materialize and the version of React I was running -- had to downgrade versions from 16.0.0 to 15.6.2 -- so I'm wondering if it's related to that. Any help would be appreciated. Thanks.
Use map instead of forEach in displayHeights method
forEach method does some operation on each element of array or collection, but does not return the modified element, map method returns the modified element after some operation
Your implemenetation has two issues
you are using forEach which does not return the modified elements
you did not return the array containing options, in your case , heights
The modified code block will be
return heights.map(function(h) {
return(<option value={h}>{h}</option>);
});
This should be your displayHeights function. Here is a WORKING DEMO
You need to return the array back in your function and also map is what you should be using.
displayHeights(){
let prices = this.props.prices;
let height;
let heights = [];
console.log(prices);
for (height in prices) {
heights.push(height)
};
console.log(heights);
return heights.map(function(h, i) {
return(<option key={i} value={h}>{h}</option>);
});
}
Using forEach
let heightsJSX = [];
heights.forEach(function(h, i) {
heightsJSX.push(<option key={i} {value={h}>{h}</option>));
});
return heights;
For efficiency's sake, why not just map over the keys. Here's a one-liner.
displayHeight() {
return Object.keys(this.props.prices).map(key => <option key={key} value={key}>{key}</option>);
}
<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.ckeditor.com/4.6.2/standard/ckeditor.js"></script>
</head>
<body>
<textarea name="editorUrdu"></textarea>
<script>
CKEDITOR.plugins.addExternal( 'easykeymap', '/ckeditor/plugins/easykeymap', 'plugin.js' );
CKEDITOR.replace( 'editorUrdu',{
extraPlugins: 'easykeymap',
contentsLangDirection: 'rtl'
});
</script>
</body>
</html>
/**
* This work is mine, and yours. You can modify it as you wish.
* #Author: Roni Saha<roni.cse#gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
CKEDITOR.plugins.add('easykeymap',
{
requires : ['wysiwygarea'],
init: function (editor) {
//var keyMaps = CKEDITOR.tools.extend({}, editor.config.easykeymaps || {}, true);
//Not using keymap specification from config variables, but using internally defined ones from param below
function isRegisteredKeystroke(code) {
if(typeof editor.keystrokeHandler.keystrokes[code] != 'undefined') {
console.warn("the keystroke : " + code + " is being attached to another event")
return true;
}
return false;
}
var sm1 = []; var en1 = [];
sm1[192]="؏"; en1[192]="~";
sm1[49]="۱"; en1[49]="1";
sm1[50]="۲"; en1[50]="2";
sm1[51]="۳"; en1[51]="3";
sm1[52]="۴"; en1[52]="4";
sm1[53]="۵"; en1[53]="5";
sm1[54]="۶"; en1[54]="6";
sm1[55]="۷"; en1[55]="7";
sm1[56]="۸"; en1[56]="8";
sm1[57]="۹"; en1[57]="9";
sm1[48]="۰"; en1[48]="0";
sm1[189]="-"; en1[189]="-";
sm1[187]="="; en1[187]="=";
//sm1[8]=""; en1[8]="";//BACKSPACE
var sm2 = []; var en2 = [];
sm2[9]=""; en2[9]="";//TAB
sm2[81]="ق"; en2[81]="Q";
sm2[87]="و"; en2[87]="W";
sm2[69]="ع"; en2[69]="E";
sm2[82]="ر"; en2[82]="R";
sm2[84]="ت"; en2[84]="T";
sm2[89]="ے"; en2[89]="Y";
sm2[85]="ء"; en2[85]="U";
sm2[73]="ی"; en2[73]="I";
sm2[79]="ہ"; en2[79]="O";
sm2[80]="پ"; en2[80]="P";
sm2[219]="'"; en2[219]="{";
sm2[221]="ﷲ"; en2[221]="}";
sm2[220]="÷"; en2[220]="\\";
var sm3 = []; var en3 = [];
//sm3[20]=""; en3[20]="";//CAPSLOCK
sm3[65]="ا"; en3[65]="A";
sm3[83]="س"; en3[83]="S";
sm3[68]="د"; en3[68]="D";
sm3[70]="ف"; en3[70]="F";
sm3[71]="گ"; en3[71]="G";
sm3[72]="ح"; en3[72]="H";
sm3[74]="ج"; en3[74]="J";
sm3[74]="ک"; en3[75]="K";
sm3[76]="ل"; en3[76]="L";
sm3[186]="؛"; en3[186]=":";
sm3[222]=","; en3[222]="\"";
sm3[13]=""; en3[13]="";//ENTER
var sm4 = []; var en4 = [];
//sm4[16]=""; en4[16]="";//SHIFT
sm4[90]="ز"; en4[90]="Z";
sm4[88]="ش"; en4[88]="X";
sm4[67]="چ"; en4[67]="C";
sm4[86]="ط"; en4[86]="V";
sm4[66]="ب"; en4[66]="B";
sm4[78]="ن"; en4[78]="N";
sm4[77]="م"; en4[77]="M";
sm4[188]="،"; en4[188]="<";
sm4[190]="۔"; en4[190]=">";
sm4[191]="/"; en4[191]="?";
sm4[16]=""; en4[16]="";//SHIFT
var keyBoard = {};
keyBoard.Row1 = { "sm" : sm1, "en" : en1 };
keyBoard.Row2 = { "sm" : sm2, "en" : en2 };
keyBoard.Row3 = { "sm" : sm3, "en" : en3 };
keyBoard.Row4 = { "sm" : sm4, "en" : en4 };
function getMappedCharacter(code) {
console.info(code);
if (typeof keyBoard.Row1.sm[code] != 'undefined')
return keyBoard.Row1.sm[code]
else if (typeof keyBoard.Row2.sm[code] != 'undefined')
return keyBoard.Row2.sm[code]
else if (typeof keyBoard.Row3.sm[code] != 'undefined')
return keyBoard.Row3.sm[code]
else if (typeof keyBoard.Row4.sm[code] != 'undefined')
return keyBoard.Row4.sm[code]
else
return false;
}
editor.on( 'key', function( event ) {
var t = event.target;
var mappedCode = getMappedCharacter(event.data.keyCode);
if (mappedCode !== false && !isRegisteredKeystroke(event.data.keyCode)) {
event.cancel();
if(typeof mappedCode == 'function') {
return mappedCode.call(editor, editor);
}
editor.insertText(mappedCode);
}
} );
}
});
I'm using ckeditor for allowing my users to input Urdu text. Like Arabic, Urdu is cursive, and uses distinct ligatures when joined.
I'm using the
editor.on( 'key', function( event ))
event to intercept the
event.data.keyCode
and replace it using
editor.insertText()
function.
So, e.g. if the user types L and A, I replace it with the Urdu
ا (U+0627)
and
ل (U+0644).
Now, after being typed, they both appear as distinct characters, separate from each other. I can press space, or enter, and they both remain as they are. But I would like them to be replaced with their proper equivalent ligature ﻻ which is FEFB in this unicode chart
I see that ckeditor automatically correct this if I switch to Source view. There, inside the <p> block, it shows the separate, disjointed letter's already replace with proper cursive ligature. And it keeps it that way when I switch back from Source view. But whatever is causing this to happen, how can I trigger that to happen while typing?
Attaching images also.
After typing
After going to source view
After returning from source view
When ever you type L and A , editor.insertText() is just append it as two separate stings, instead of combining into one.
<p>"ل"
"ا"
<br>
</p>
that why its not producing desired output.
Added these two line
var $pTag = editor.getSelection().getRanges()[0].startContainer.$; // accessing the p tag
$pTag.innerText = $pTag.innerText+mappedCode; // modifing the inner text
replacing
editor.insertText(mappedCode); // in editor.on( 'key', function( event )
will output as "لا"
above fix has some issues to deal with like linebeak(new line)
Updated
replace the below snippet
var $pTag = editor.getSelection().getRanges()[0].startContainer.$;
var innerText =$pTag.innerText; // accessing the p tag data
$pTag.innerText = ""; // removing the existing data
editor.insertHtml(innerText+mappedCode); // concat with new string
with
editor.insertText(mappedCode); // in editor.on( 'key', function( event )
Example: codepen
I'm having trouble finding a solution to this. How can I convert the code below into something more dynamic and succinct?
OneComponent = require ('./OneComponent')
TwoComponent = require ('./TwoComponent')
ThreeComponent = require ('./ThreeComponent')
Example = React.createClass
render: ->
filter = #props.filterState
if filter is 'something'
tableCmpt =
<div>
<OneComponent
tasks={#props.tasks}
/>
</div>
if filter is 'somethingElse'
tableCmpt =
<div>
<TwoComponent
tasks={#props.tasks}
/>
</div>
##... etc
return tableCmpt
I've done something like this.
var Components = {
'something': require('./Component'),
'somethingElese': require('./Component2')
};
Example = React.createClass({
render: function() {
var component = Components[this.props.filter];
return <div><component tasks={this.props.tasks}/></div>;
}
});
I couldn't get Crob's answer to work (though I appreciate the hash table idea), but it led me to find a solution - I just skipped the jsx step and used the compiled js:
React.createElement(component, {tasks: this.props.tasks} )
So all in all:
var Components = {
'something': require('./Component'),
'somethingElese': require('./Component2')
};
Example = React.createClass({
render: function() {
var component = Components[this.props.filter];
React.createElement(component, {tasks: this.props.tasks} )
}
});
I am able to take screenshot of the page using the example code below:
html2canvas(document.body, {
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});
Now there are certain div's i dont want to be part of the page when I take the screenshot?
How can i prevent them from being part of the screenshot.
One way I thought was to clone the element and then remove the elements, but taking a screenshot of the clone gives a white screen. Here is the code I used:
html2canvas($(document.body).clone()[0], {
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});
Add this attribute: data-html2canvas-ignore to any element you don't want to be taken when the screenshot is processed.
Hopefully this will help the next guy.
When I used this library I faced a problem that the lib download all the images in my application, that cause the application to run slowly. I resolved the problem using the ignoreElements option.
This is my code:
var DropAreaElement= document.getElementById("123");
var config= {
useCORS: true,
ignoreElements: function (element) {
if (element.contains(DropAreaElement) || element.parentElement.nodeName =="HTML" || element == DropAreaElement || element.parentNode == DropAreaElement) {
console.log("elements that should be taken: ", element)
return false;
}else {
return true;
}
}
};
html2canvas(DropAreaElement, config).then(function (canvas){
var imgBase64 = canvas.toDataURL('image/jpeg', 0.1);
console.log("imgBase64:", imgBase64);
var imgURL = "data:image/" + imgBase64;
var triggerDownload = $("<a>").attr("href", imgURL).attr("download", "layout_" + new Date().getTime() + ".jpeg").appendTo("body");
triggerDownload[0].click();
triggerDownload.remove();
}).catch(Delegate.create(this, function (e){
console.error("getLayoutImageBase64 Exception:", e);
});
If you don't want to use an attribute, html2canvas does provide a method to remove elements. For example:
html2canvas( document.body, {
ignoreElements: function( element ) {
/* Remove element with id="MyElementIdHere" */
if( 'MyElementIdHere' == element.id ) {
return true;
}
/* Remove all elements with class="MyClassNameHere" */
if( element.classList.contains( 'MyClassNameHere' ) ) {
return true;
}
}
} ).then( function( canvas ) {
document.body.appendChild( canvas );
} );
For more information, see html2canvas options.
You can create HOC for <Printable/> and <NonPrintable/> , you can wrap your component with <NonPrintable><YourCoolComponent/></NonPrintable>
those children components would be excluded.
import React from "react"
interface INonPrintable {
children: React.ReactChildren
}
/*
HOC - Printable which injects the printId to the React component
which gets us Printable Context to html2canvas => jsPDF
eg:
<Printable printId="about-you-print">
<PersonalInfo badEmail={badEmail} />
<IdentityInfo />
<AdditonalInfo />
<AddressInfo
serviceAddress={serviceAddress}
billingAddress={this.state.billingAddress}
setBillingAddress={this.setBillingAddress}
/>
</Printable>
*/
export default function Printable({ printId = "", children, ...restProps }) {
return <div print-id={printId} {...restProps}>{children}</div>
}
/*
HOC - NONPrintable which injects the data-html2canvas-ignore to the React component
which gets us Printable Context to html2canvas => jsPDF
eg:
<NonPrintable style={{display:"flex",justifyContent:'space-around'}}>
<Button
text="Print PDF using Own utility"
onClick={this.handlePrintPdf}
/>
<Button
text="Print PDF using html2canvas + jsPDF"
onClick={this.handlePrintwithPDFjs}
/>
</NonPrintable>
*/
export const NonPrintable = ({ children, ...restProps }) => {
return <div data-html2canvas-ignore {...restProps}>{children}</div>
}
Let's say I have a website where for each main section I have a specific sidebar.
Currently I have a single sidebar file, where im using categories to filter the correct content to show like this:
{{#inArray page.categories "Components"}}
<section class="sg-index">
<ul>
<li {{#is title "DetailContent"}} class="active"{{/is}}>
DetailContent
</li>
However my goal is to have these sidebar files located at each section folder, along with the section files.
How can I include the {{dirname}} variable in the partials call {{> sidebar}}?
This should be possible with a conditional block helper, like {{with}} and the include helper,
B you could also create a custom helper, something like this:
var path = require('path');
var _ = require('lodash');
var matter = require('gray-matter');
module.exports.register = function (Handlebars, options, params) {
var assemble = params.assemble;
var grunt = params.grunt;
var opts = options || {};
Handlebars.registerHelper('sidenav', function(page, context) {
if(page.published !== false && page.sidenav) {
if(!Array.isArray(assemble.partials)) {
assemble.partials = [assemble.partials];
}
var filepath = _.first(_.filter(assemble.partials, function(fp) {
return path.basename(fp, path.extname(fp)) === page.sidenav;
}));
// Process context, using YAML front-matter,
// grunt config and Assemble options.data
var pageObj = matter(filepath) || {};
var metadata = pageObj.context || {};
context = _.extend(this, opts.data[page.sidenav], metadata, context);
var partial = Handlebars.partials[page.sidenav];
var template = Handlebars.compile(partial);
var output = template(context);
// Prepend output with the filepath to the original partial
// for debugging
var sidenav = opts.sidenav || opts.data.sidenav || {};
if(sidenav.origin === true) {
output = '<!-- ' + filepath + ' -->\n' + output;
}
return new Handlebars.SafeString(output);
}
});
};
then use it in your markup like this:
<div class="sidebar" role="complementary">
<ul class="nav sidenav">
{{sidenav this}}
</ul>
</div>