Styling Polymer paper-slider - javascript

So I'm essentially wrapping the standard paper-slider element with a custom element, and wanting to include some styling. Below is what I currently have:
<dom-module id="my-slider">
<template>
<style>
/* Works */
paper-slider {
--paper-slider-active-color: red;
--paper-slider-knob-color: red;
--paper-slider-height: 3px;
}
/* Doesn't work */
paper-slider #sliderContainer.paper-slider {
margin-left: 0;
}
/* Also doesn't work */
.slider-input {
width: 100px;
}
</style>
<div>
<paper-slider editable$="[[editable]]" disabled$="[[disabled]]" value="{{value}}" min="{{min}}" max="{{max}}"></paper-slider>
</div>
</template>
<script>
Polymer({
is: "my-slider",
properties: {
value: {
type: Number,
value: 0,
reflectToAttribute: true
},
min: {
type: Number,
value: 0
},
max: {
type: Number,
value: 100
},
editable: Boolean,
disabled: Boolean
}
});
</script>
</dom-module>
JSFiddle: https://jsfiddle.net/nhy7f8tt/
Defining the variables that the paper-slider uses works as expected, but when I try to address anything inside of it directly via its selector, it doesn't work.
I'm fairly new to Polymer, so this may be a very simple/stupid question, but I'm really quite confused and would greatly appreciate any help!
A secondary (but related) issue is the clipping of the input to the right of the editable paper-slider element when the value is 100.

You could use selectors like ::shadow and /deep/ but they are deprecated. If an element doesn't provide the hooks (CSS variables and mixins) then you're basically out of luck.
What you can do, is to create a feature request in the elements GitHub repo to support additional selectors.
Another workaround I already used successfully is to add a style module.
var myDomModule = document.createElement('style', 'custom-style');
myDomModule.setAttribute('include', 'mySharedStyleModuleName');
Polymer.dom(sliderElem.root).appendChild(myDomModule);
I hope the syntax is correct. I use Polymer only with Dart.
The dom-module needs to be a custom-style even though this is normally only necessary when used outside a Polymer element.
See also https://github.com/Polymer/polymer/issues/2681 about issues I run into with this approach.

The following code is working for me,
attached: function(){
var sliderContainer = document.querySelector("#sliderContainer.editable.paper-slider");
sliderContainer.style.marginTop = "5px";
sliderContainer.style.marginBottom = "5px";
},

Related

Avoid flickering when changing existing DOM elements on page load event

I have the following DOM element change function using vanilla JavaScript where I change the span element that contains some text string of the DOM with a page load event.
With the following code, the DOM elements are changed as expected. However, they still see a minimal fraction of flickering before the DOM element change for the variable desktop and mobile.
Example of the flickering scenario:
I see the tag span "Text I want to change" for a fraction of second.
I see the changed tag span "text1 changed" after the content has fully loaded.
I think this is happening because the DOM changes are applied when the DOM content of the page is fully loaded. I would like to hide the existing tag elements until the changes have been applied and then display them.
The elements structure for desktop and mobile I want to manipulate is like the following:
<span class="first-headline target-span">Text I want to change</span>
See the concept below:
var changesObj = {
"us": {
title: "text1 changed"
},
"eu": {
title: "text2 changed"
}
};
function changes() {
var webshop = changesObj[window.location.pathname.split('/')[1]];
console.log(webshop);
var changesText;
if (!webshop) {
console.log('webshop not found');
}
changesText = webshop.title;
if (document.querySelector('.target-span').innerText.length > 0) {
var desktop = document.querySelector('.target-span');
console.log(desktop);
desktop.innerText = changesText;
console.log(desktop.innerText);
console.log("applied changes for dekstop");
}
if (document.querySelector('.target-span').innerText.lenght > 0) {
var mobile = document.querySelector('.target-span');
mobile.innerText = changesText;
console.log(mobile.innerText);
console.log("applied changes for mobile");
}
}
function invokeChanges() {
document.addEventListener("DOMContentLoaded", function () {
changes();
});
}
invokeChanges();
Is there a way to initially hide the DOM element until the change to the existing element has been applied and then show it?
I was thinking of using something inline CSS rules like the following:
Set .style.visbility='hidden' and when the text content is changed to show it with .style.visbility='visble'.
But I'm not sure how this solution should be appropriately implemented in my code.
There are a few reasons as to why it's flickering, but two preventative steps you can take:
Use defer on your script tag <script defer>, as this lets the browser handle the order your scripts are run instead of DOMContentLoaded. You also get to avoid the changes wrapper function.
As suggested by this question (and your own reasoning) attach inline CSS / a CSS file to the page to set the text as invisible, then mark it as visible
If it doesn't affect the layout of your page too much, you can also opt to dynamically generate the element instead.
However, do note that regardless of any of these, these still require JS to be executed and may still have a delay. If you are able to use it, you may be interested in prerendering your webpage - from your JS code, it looks for the route name if its eu or us, which means your page is pre-renderable.
Quick & Dirty:
Place the script tag just below your span tag:
<span class="first-headline target-span">Text I want to change</span>
<script>
var changesObj = {
"us": {
title: "text1 changed"
},
"eu": {
title: "text2 changed"
}
};
function changes() {
var webshop = changesObj[window.location.pathname.split('/')[1]];
console.log(webshop);
var changesText;
if (!webshop) {
console.log('webshop not found');
}
changesText = webshop.title;
if (document.querySelector('.target-span').innerText.length > 0) {
var desktop = document.querySelector('.target-span');
console.log(desktop);
desktop.innerText = changesText;
console.log(desktop.innerText);
console.log("applied changes for dekstop");
}
if (document.querySelector('.target-span').innerText.lenght > 0) {
var mobile = document.querySelector('.target-span');
mobile.innerText = changesText;
console.log(mobile.innerText);
console.log("applied changes for mobile");
}
}
changes();
</script>
This way you avoid the asynchronous code part.
In any case you need to wait until the DOM has been loaded before you can manipulate it. Using an even listener for DOMContentLoaded would be the way to go. So, three things need to happen:
Wait for the DOM to load
Find the elements and change the text
Make the elements visible. You can either use the property visibility: hidden or display: none. Difference is that with visibility: hidden the element will still take up space. So, the choice depends on the context.
In the first example I add a timeout so that you can see what the page looks like just before the text is changed. I also styled the <p> (display: inline-block) so that you can see the size of the hidden span.
window.location.hash = "us"; // for testing on SO
var changesObj = {
"us": { title: "text1 changed" },
"eu": { title: "text2 changed" }
};
function changes(e) {
//let webshop = changesObj[window.location.pathname.split('/')[1]];
let webshop = changesObj[window.location.hash.substring(1)]; // for testing on SO
if (webshop) {
[...document.querySelectorAll('.target-span')].forEach(span => {
span.textContent = webshop.title;
span.classList.add('show');
});
}
}
document.addEventListener('DOMContentLoaded', e => {
setTimeout(changes, 1000);
});
p {
border: thin solid black;
display: inline-block;
}
.target-span {
visibility: hidden;
}
.target-span.show {
visibility: visible;
}
<p>
<span class="first-headline target-span">Text I want to change</span>
</p>
<p>
<span class="first-headline target-span">Text I want to change</span>
</p>
In the second example I combined all the code into one HTML page. Notice that the style is defined in the header. So, when the DOM is passed the CSS will be passed as well without having to load a external style-sheet (well, there are tricks for that as well, but out of scope for this answer).
<html>
<head>
<style>
p {
border: thin solid black;
}
.target-span {
visibility: hidden;
}
.target-span.show {
visibility: visible;
}
</style>
<script>
window.location.hash = "us"; // for testing on SO
var changesObj = {
"us": {title: "text1 changed"},
"eu": {title: "text2 changed"}
};
function changes(e) {
//let webshop = changesObj[window.location.pathname.split('/')[1]];
let webshop = changesObj[window.location.hash.substring(1)]; // for testing on SO
if (webshop) {
[...document.querySelectorAll('.target-span')].forEach(span => {
span.textContent = webshop.title;
span.classList.add('show');
});
}
}
document.addEventListener('DOMContentLoaded', changes);
</script>
</head>
<body>
<p>
<span class="first-headline target-span">Text I want to change</span>
</p>
<p>
<span class="first-headline target-span">Text I want to change</span>
</p>
</body>
</html>

Multiple instances of CodeMirror textarea on the same page

I'm trying to make a little project for job interview questions and have a bunch of questions with answers and code examples on the page. Each is inside a .collapsible div from Materialize.css, and when clicked shows the answer and code example.
What is the best way to go about this? I tried putting initializer into a function, grabbing all my textareas form the DOM, looping through them and turning them into CodeMirror textareas.
$(document).ready(function(){
var blocks = document.getElementsByClassName('code-block');
function createEditorFrom(selector) {
let editor = CodeMirror.fromTextArea(selector, {
lineNumbers : false,
mode: "swift",
});
}
for (var x = 0; x < blocks.length; x++) {
createEditorFrom(blocks[x]);
}
// Callback for Collapsible open
$('.collapsible').collapsible({
onOpen: function() {
// call editor.refresh()?
},
});
});
This does work but I feel like it is not a very elegant way of solving this issue. Is there a better way to do this?
Questions:
Is there a better way to create all the CodeMirror textareas?
The code in the editors does not appear until clicked. Nothing I do makes it work. Calling editor.refresh() (with setTimeout), and autorefresh: true.
One thing you will have to do anyhow is keeping a reference to your CodeMirror instances (eg. to get/set their value), so createEditorForm will have to return the CodeMirror instance, & you could for example push them to an array in the loop:
function createEditorFrom(selector) {
return CodeMirror.fromTextArea(selector, {
lineNumbers : false,
mode: "swift",
});
}
let codeblocks = [];
for (var x = 0; x < blocks.length; x++) {
codeblocks.push({
CM: createEditorFrom(blocks[x]),
el: blocks[x]
});
}
I've encountered a similar problem to "The code in the editors does not appear until clicked" and I even wrote a comment next to the solution. Translated to your implementation that would be:
function createEditorFrom(selector) {
var instance = CodeMirror.fromTextArea(selector, {
lineNumbers : false,
mode: "swift",
});
// make sure the CodeMirror unit expands by setting a null character
instance.setValue('\0');
instance.setValue('');
return instance;
}
For what it's worth, I also used some CSS tweaks to ensure consistent initial & max-height rendering:
/* CodeMirror */
.CodeMirror { height: auto; overflow: auto; max-height: 250px; }
.CodeMirror-scroll { height: auto; min-height: 24px; }
Is there a better way to create all the CodeMirror textareas [than by looping through them]?
Yes, you can lazy initialize them if they are not visible on page load (and actually I believe it's better to initialize them only when the parent node of the <textarea> is visible, I'm not sure if CodeMirror requires render-info (like Element.getBoundingClientRect), e.g. only load them onOpen of their collapsible parent.
I ended up initializing each one when the collapsible is opened. This works but I guess it would need to be adjusted if there were multiple CodeMirror textareas in the same collapsible.
I also added in a check becuase otherwise when the collapsible gets opened, then closed, then reopened it would create a duplicate textarea.
$(document).ready(function(){
$('.collapsible').collapsible({
onOpen: createCodeBlock
});
function createCodeBlock(target) {
var card = target.context.parentElement.parentElement;
var textarea = card.getElementsByClassName('code-block')[0];
// Placeholder class that prevents the duplicate creation of
// the same textarea when the collapsible is closed then reopened.
if (!textarea.classList.contains("created")) {
CodeMirror.fromTextArea(textarea, {
lineNumbers: false,
mode: "swift",
});
textarea.className += "created";
}
}
});

Disable/Enable CSS on webpage using Javascript

According to This page I was able to remove all the CSS preloaded and added on the webpage using that. I wanted to implement a button system where "onclick" = enable/disable webpage CSS even the ones pre-loaded by my web-host. I would like to eliminate the style tags to prevent lags for my website users. I prefer using the script that I have linked above unless there is another alternative that works better. Is it possible to enable CSS onclick the same button to disable? If not is it possible, can it be done with this quick? example with the preferred script below:
if (disable) {
style = "disable";
} else {
location.reload();
}
PREFERRED SCRIPT:
function removeStyles(el) {
el.removeAttribute('style');
if(el.childNodes.length > 0) {
for(var child in el.childNodes) {
/* filter element nodes only */
if(el.childNodes[child].nodeType == 1)
removeStyles(el.childNodes[child]);
}
}
}
removeStyles(document.body);
What about a different aproach?
Add initially a class to a body called 'styled' for example
<body class="styled">
use it as a main selector in your css definitions
<style>
.styled a { ... }
.styled h1 { .... }
</style>
then an example jquery script to toggle the class:
<script>
$(function() {
$('#myswitch').click(function() {
$('body').toggleClass('styled');
});
});
</script>
when class is present, the page will be styled, when absent there will be no styling.
Of coures there could be better aproach, but this is the first thing which pops up in my mind
To remove all style on an element, you could do
function removeStyles(el) {
el.style = {};
}
If you want to enable/disable the CSS on the page, then the goal is not to merely remove all the styles on the page, but you will need to save them somewhere also so they can be recalled when the user re-clicks the button. I would recommend having jQuery to help you with this, and it could be done the following way:
var style_nodes = $('link[rel="stylesheet"], style');
style_nodes.remove();
$('*').each(function(num, obj) {
var style_string = $(obj).attr("style");
if (style_string) {
$(obj).data("style-string", style_string);
$(obj).attr("style", "");
}
});
Now you've saved the stylesheets and style DOM nodes inside of style_nodes, and the actual style attribute inside of a jQuery data attribute for that specific DOM node. When you click to add the CSS back to the page, you can do the following:
$('head').append(style_nodes);
$('*').each(function(num, obj) {
if ($(obj).data("style-string"))
$(obj).attr("style", $(obj).data("style-string"));
});
Check out this JS Fiddle I put together to demonstrate it:
https://jsfiddle.net/5krLn3w1/
Uses JQuery, but I'm sure most frameworks should give you similar functionality.
HTML:
<h1>Hello World</h1>
Turn off CSS
Turn on CSS
JS:
$(document).ready(function() {
$('a#turn_off').click(function(evt) {
evt.preventDefault();
var css = $('head').find('style[type="text/css"]').add('link[rel="stylesheet"]');
$('head').data('css', css);
css.remove();
});
$('a#turn_on').click(function(evt) {
evt.preventDefault();
var css = $('head').data('css');
console.info(css);
if (css) {
$('head').append(css);
}
});
});
CSS:
body {
color: #00F;
}
h1 {
font-size: 50px;
}

jQuery Minicolors - How to write value in HTML?

I found here on Stackoverflow similar topics but not the same.
I am using jquery Minicolors on my project: https://github.com/claviska/jquery-miniColors/
Everything work fine, but I need to do easy thing - I need to get actual color value and write it to the HTML document. So for example I need to do this:
<p><script>document.write(actualColor);</script></p>
Simply just write the actual color code anywhere in static HTML. I am very bad in JS and I didnt find some working solution.
My minicolors code look like this:
<script>
$(document).ready( function() {
$('.color-picker-1').each( function() {
$(this).minicolors({
defaultValue: $(this).attr('data-defaultValue') || '',
inline: $(this).attr('data-inline') === 'true',
});
});
});
</script>
And I use just this input for picking color:
<input type="text" id="inline" class="color-picker-1" data-inline="true" value="#4fc8db">
What can I do? I guess solution is very easy.
On change of the color, re-assign the value to the input element so that it contains new value. Then you can use that value. If you want, you can create another hidden input element to store the color values as well.
'change' event gets triggered when color value is changed. This is where all DOM manipulation should go into.
$("#inline").minicolors({
change: function(hex, opacity) {
$("#inline").val(hex);
}
});
On your code:
<script>
$(document).ready( function() {
$('.color-picker-1').each( function() {
$(this).minicolors({
defaultValue: $(this).attr('data-defaultValue') || '',
inline: $(this).attr('data-inline') === 'true',
change: function(hex, opacity) {
$(this).val(hex);
}
});
});
});
</script>

Get CodeMirror instance

I want to get an instance of CodeMirror (it is binded to a textarea '#code'). From an onclick-event I want to add a value to the current value of the CodeMirror instance. How can this be achieved? From the docs I can't seem to find anything to get an instance and bind it to a loca var in javascript.
Another method I have found elsewhere is as follows:
//Get a reference to the CodeMirror editor
var editor = document.querySelector('.CodeMirror').CodeMirror;
This works well when you are creating the CodeMirror instance dynamically or replacing an existing DOM element with a CodeMirror instance.
Someone just posted an answer but removed it. Nevertheless, it was a working solution.
Thanks!
-- Basically this was his solution:
// create an instance
var editor = CodeMirror.fromTextArea('code');
// store it
$('#code').data('CodeMirrorInstance', editor);
// get it
var myInstance = $('code').data('CodeMirrorInstance');
// from here on the API functions are available to 'myInstance' again.
There is a getWrapperElement on code mirror editor objects which gives you the root DOM element of the code mirror instance:
var codemirrorDomElem = editor.getWrapperElement();
You can find the instance starting with the <textarea> and moving to the next sibling.
Native
Functional
document.querySelector('#code').nextSibling,
Selector
document.querySelector('#code + .CodeMirror'),
jQuery
Functional
$('#code').next('.CodeMirror').get(0),
Selector
$('#code + .CodeMirror').get(0)
Extra: A more advanced solution involving clipboard.js -> JSFiddle Demo
Example
// Selector for textarea
var selector = '#code';
$(function() {
var editor = CodeMirror.fromTextArea($(selector).get(0), {
mode: 'javascript',
theme: 'paraiso-dark',
lineNumbers : true
});
editor.setSize(320, 240);
editor.getDoc().setValue(JSON.stringify(getSampleData(), null, 4));
$('#response').text(allEqual([
document.querySelector(selector).nextSibling, // Native - Functional
document.querySelector(selector + ' + .CodeMirror'), // Native - Selector
$(selector).next('.CodeMirror').get(0), // jQuery - Functional
$(selector + ' + .CodeMirror').get(0) // jQuery - Selector
]));
});
function allEqual(arr) {
return arr.every(function(current, index, all) {
return current === all[(index + 1) % all.length];
});
};
// Return sample JSON data.
function getSampleData() {
return [
{ color: "red", value: "#f00" },
{ color: "green", value: "#0f0" },
{ color: "blue", value: "#00f" }
];
}
#response { font-weight: bold; }
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.7.0/codemirror.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.7.0/theme/paraiso-dark.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.7.0/codemirror.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>All equal?: <span id="response"></span></div>
<textarea rows="10" cols="60" id="code"></textarea>
I am using with Vue CLI 3, vue-codemirror component like this:
<codemirror id="textarea_editor" v-model="textarea_input" :options="cm_editor_config"></codemirror>
import codemirror to use within page:
import { codemirror } from 'vue-codemirror'
import 'codemirror/lib/codemirror.css'
and simply add codemirror inside components object, thereafter configuration in data section is:
codemirror_editor: undefined,
cm_editor_config: {
tabSize: 4,
mode: 'application/json',
theme: 'base16-dark',
lineNumbers: true,
// lineWrapping: true,
styleActiveSelected: true,
line: true,
}
and initialized the object in mounted lifecycle section of Vue:
mounted () {
if ( !this.codemirror_editor ) {
this.codemirror_editor = $('#textarea_editor').find('.CodeMirror').get(0).CodeMirror;
}
// do something with this.codemirror_editor
}
Hope this would help many one.
You can simply drop the var: instead of having
var editor = CodeMirror.fromTextArea...
Just have
editor = CodeMirror.fromTextArea...
Then editor is directly available to use in other functions

Categories

Resources