I'm building a UI library in JS that can, without relying on any CSS stylesheets, create UI components, stylised from code. So far, it's been quite easy, with exception of styling different control states (such as input:focus one).
Code that I use to create input field:
function newInput()
{
var ctr = docmuent.createElement("input");
ctr.setAttribute("type","text");
ctr.setAttribute("value", some-default-value);
ctr.style.fontFamily = "sans-serif,helvetica,verdana";
/* some font setup here, like color, bold etc... */
ctr.style.width = "256px";
ctr.style.height = "32px";
return ctr;
}
Styling it for default state is easy. However I am unsure how to set style for states such as focused, disabled or not-editable.
If I'd be having CSS stylesheets included in the project that would be easily sorted out. However I can't have any CSS files included, it must be pure JS.
Does anyone know how to set style for an input field state (eg. input:focus) straight from JS code?
No JQuery please :-) Just straight-up JS.
Thanks in advance!
You would need to add an event listener to the element in order to change the style of it. Here is a very basic example.
var input = document.getElementById("something");
input.addEventListener("focus", function () {
this.style.backgroundColor = "red";
});
<input type="text" id="something" />
Other alternative would be to build a stylesheet for the page.
Something like this:
var styles='input:focus {background-color:red}';
var styleTag=document.createElement('style');
if (styleTag.styleSheet)
styleTag.styleSheet.cssText=styles;
else
styleTag.appendChild(document.createTextNode(styles));
document.getElementsByTagName('head')[0].appendChild(styleTag);
This way you will have clean separation of css styles from the scripts and so the better maintenance.
Use CSS Variables if possible
It's 2022 and there are more simple solutions to this problem than adding event listeners all over the place that may never get cleaned up.
Instead if you have control over the CSS simply do this in your CSS:
.my-class {
--focusHeight: 32px;
--focusWidth: 256px;
}
.my-class:focus {
height: var(--focusHeight);
width: var(--focusWidth);
}
Then in your JavaScript it's as simple as using setProperty to update the variables:
const el = document.getElementById('elementId');
el.style.setProperty('--focusHeight', newFocusHeight);
el.style.setProperty('--focusWidth', newFocusWidth);
At first, create your input:
<input type="text" id="myElementID" />
Then add the javascript the following javascript:
const element = document.getElementById("myElementID");
// Add a box shadow on focus
element.addEventListener("focus", (e) => {
e.target.style.boxShadow = "0 0 0 3px #006bff40";
});
// Remove the box shadow when the user doesn't focus anymore
element.addEventListener("blur", (e) => {
e.target.style.boxShadow = "";
});
A quick oneliner, which dynamically appends a style tag to the
body.
document.body.innerHTML += '<style>#foo:focus {background-color:gold}</style>'
<input id="foo"/>
let input = document.querySelector(".input-text");
let label = document.querySelector('.fields-label');
input.addEventListener('focus', function(e){
label.classList.add('is-active');
})
input.addEventListener('blur', function(e){
if(input.value === "") {
label.classList.remove('is-active');
}
})
label.is-active{
color:red;
}
<div class="input-fields">
<label class="fields-label">Last Name</label>
<input type="text" class="input-text">
</div>
Related
I am trying to replicate the jQuery width function using pure JavaScript. Pure JavaScript functions such as getComputedStyle or offsetWidth seem to work if the element is visible, but I cannot replicate the same behavior when the element is hidden.
It seems that jQuery is doing something different here and I cannot figure out what exactly.
To clearly explain what i am trying to do, Here is a codepen example where I try the getComputedStyle in comparison with the jQuery width function for calculating the width of a hidden element that is changing dynamically.
const input = $('input');
const sizer = $('.sizer');
const sizerDom = document.querySelector('.sizer');
input.on('input', evt => {
sizer.text(evt.target.value);
console.log(sizer.width())
console.log(getComputedStyle(sizerDom, null).width);
});
.sizer {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text">
<span class="sizer">
https://codepen.io/OmranAbazid/pen/OJNXyoG
That is because in jQuery's internal logic, it interally swaps the display property from none to another value so that it forces the browser to momentarily render it. Otherwise the measurements will always be 0 since the element is never rendered.
Also, instead of trying to use window.getComputedStyle which will return a string value of the CSS dimension (e.g. 100px), you can use sizerDom.getBoundingClientRect() to get the actual number instead (which returns, say, 100) without needing to do additional parsing.
const input = $('input');
const sizer = $('.sizer');
const sizerDom = document.querySelector('.sizer');
input.on('input', evt => {
sizer.text(evt.target.value);
console.log(sizer.width())
const cachedDisplay = window.getComputedStyle(sizerDom).display;
sizerDom.style.display = 'inline-block';
console.log(sizerDom.getBoundingClientRect().width);
sizerDom.style.display = cachedDisplay;
});
.sizer {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text">
<span class="sizer">
So I'm trying to make a code that allows me to change the image once I hover over it.
The initial code works. But I have 72 other images to go through with this feature. I'm trying to call each one individually so I don't have to repeat so much code.
I want a simple html code like
<img id="seal" src="img/seal/dantalion.png" onmouseover="rollover(dantalion)"
onmouseout="rollaway(dantalion)" />
<img id="seal" src="img/seal/vassago.png" onmouseover="rollover(vassago)"
onmouseout="rollaway(vassago)" />
Here is the code that works.
function rollover(img) {img.src = "img/seal/hover/vassago.png";}
function rollaway(img) {img.src = "img/seal/vassago.png";}
Here is what I want to do. Keep in mind please, I'm new to this sort of thing.
function rollover() {
dantalion.src = "img/seal/hover/dantalion.png";
vassago.src = "img/seal/hover/vassago.png";
}
function rollaway() {
dantalion.src = "img/seal/dantalion.png";
vassago.src = "img/seal/vassago.png";
}
How do I individually call the object in the HTML code?
This is a simple solution to your problem. On mouseover of the wrapper div #images we check if you are hovering an image and if so, update the image src with the hover url.
On mouseout or if you hover on a different image the images are reset back to the original src
var images = document.getElementById('images');
var prevEl;
function resetImages() {
if (prevEl) {
prevEl.src = prevEl.src.replace('/hover', '');
prevEl = null;
}
}
images.addEventListener('mouseover', function(e) {
resetImages();
if (e.target && e.target.nodeName == "IMG") {
prevEl = e.target;
e.target.src = e.target.src.replace('/seal', '/seal/hover');
}
});
images.addEventListener('mouseout', resetImages);
<div id="images">
<img src="img/seal/dantalion.png">
<img src="img/seal/vassago.png">
</div>
Here's an example using event delegation. I've tried to use only core JS APIs because you didn't mention any libraries but if you were using a library there's a good chance it would be able to do some of this for you.
document.body.addEventListener('mouseover', function(ev) {
var target = ev.target,
cls = target.classList;
if (!cls.contains('seal')) {
return;
}
cls.add('seal-over');
target.innerHTML = target.innerHTML.replace('/seal/', '/seal/hover/');
});
document.body.addEventListener('mouseout', function(ev) {
var target = ev.target,
cls = target.classList;
if (!cls.contains('seal')) {
return;
}
cls.remove('seal-over');
target.innerHTML = target.innerHTML.replace('/hover', '');
});
.seal {
border: 1px dotted #777;
height: 70px;
margin: 10px;
width: 200px;
}
.seal-over {
background: #eee;
}
<div class="seal">img/seal/dantalion.png</div>
<div class="seal">img/seal/vassago.png</div>
While my example changes the innerHTML you would change the src instead - I didn't have your images so I couldn't easily use img tags. The id attribute has to be unique so I've changed it to using a class instead. Adding the seal-over class isn't required, I just thought it made the example more interesting: you'd probably use a CSS :hover pseudo-class for that if this were real code.
One of many guides to delegation, you can find others with a quick search online:
https://davidwalsh.name/event-delegate
The idea is to add a single listener on an element higher up the tree. Events propagate up so you can react to events on descendent elements. The event target refers to the element on which the actual event occurred. The code checks whether this element is one of the elements we care about and then makes the changes accordingly.
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;
}
I am trying to create a single textbox form on a webpage to boolean test the input submitted by the user. The input will be the website user's zip code. I want to make a predetermined array of zip codes that will test true.
If it is true (the zip code entered is included in the predetermined array), I want to display one bit of HTML, and if it tests false, I want to display another bit.
I've searched around and looked in some of my JavaScript books (I've just started learning) and haven't found an answer; could someone help me out with this? Thanks!
HTML:
<label id="input-label" class="invalid">
ZIP code: <input id="zipcode" />
<div class="valid-message">
Valid
</div>
<div class="invalid-message">
Invalid
</div>
</label>
CSS:
#input-label.valid .valid-message { display: block; }
#input-label.valid .invalid-message { display: none; }
#input-label.invalid .valid-message { display: none; }
#input-label.invalid .invalid-message { display: block; }
Javascript
function isValidZip(z) {
return ['12345','67890'].indexOf(z) != -1;
}
var label = document.getElementById('input-label');
var input = document.getElementById('zipcode');
input.onkeydown = function() {
label.className = isValidZip(input.value) ? "valid" : "invalid";
}
You could try something like this(might be a little off I'll double check then get back to you :) ):
var zipArr = ['98671','97006'];//insert the zip/post codes here
var userInputEl = document.getElementById('userInput');//Get the element could use tag names or it would be better actually if you used jQuery but yeah
var elToShow = document.getElementById('elementToShowIfNotFound');
var otherElToShow = document.getElementById('idOfOtherelementToShow');
var userInput = userInputEl.value();//might be .text()
if(zipArr.indexOf(userInput) === -1){//-1 means it isn't found in the array
zipArr.push(userInput);//pushes the new one onto the array
elToShow.style.display = 'block';//this changes the css display to block instead of it being hidden.
}else{
otherElToShow.style.display= 'block';
}
Might not be the best way to do this, but I'd suggest using jQuery it makes this process a lot easier.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
input type=file show only button
The has this kind of design:
Can I modify it so it won't show the text field?
a very good guide is found in quirksmode - Styling an input type="file"
quote with some modifications to match question:
Take a normal <input type="file"> and put it in an element with position: relative. or absolute
To this same parent element, add an image or a button, which have the correct styles. Position this element absolutely, so
that they occupy the same place as the <input type="file">.
Set the z-index of the <input type="file"> to 2 so that it lies on top of the styled image or button.
Finally, set the opacity of the <input type="file"> to 0. The <input type="file"> now becomes effectively invisible, and the styled
image or button shines through, but you can still click on the "Browse"
button. (Note that you can't use visibility: hidden, because a truly
invisible element is unclickable, too, and we need the <input
type="file"> to remain clickable)
Suggestion: You can use the uploadify plugin.
Don't see a jQuery tag in your question but hey, it's helpful, and possibly quite easy to rewrite in vanilla JS. This is a little jQuery plugin I extracted from Ideal Forms, a plugin I maintain at github. It covers all the basics to do what you want, with fallback for IE and multiple for HTML5 browsers. Plus handling events and markup replacement. CSS is on your own, but nothing too complicated to style as you can see. You can hide the text field too if you want. The idea here is that this allows for ANY customization possible with CSS.
$.fn.toCustomFile = function () {
return this.each(function () {
var
$file = $(this), // The file input
// Necessary markup
$wrap = $('<div class="wrap">'),
$input = $('<input type="text" class="filename" />'),
$button = $('<button type="button" class="upload">Open</button>')
// Hide by shifting to the left, that way can
// still use events that are otherwise problematic
// if the field is hidden as in "display: none"
$file.css({
position: 'absolute',
left: '-9999px'
})
// Events
$button
.attr('tabIndex', -1) // disable focus on button for better usability
.click(function () {
$file.trigger('click') // Yes, `click`, not `change`. Crossbrowser compat.
})
$file
.attr('tabIndex', -1)
.on({
change: function () {
// Detect if browser supports HTML5 "file multiple"
var multipleSupport = typeof $('input')[0].multiple !== 'undefined',
files = [],
fileArr,
filename
if (multipleSupport) {
fileArr = $file[0].files
for (var i = 0, len = fileArr.length; i < len; i++)
files.push(fileArr[i].name)
filename = files.join(', ')
} else {
filename = $file.val().split('\\').pop() // Remove fakepath
}
$input.val(filename)
// Set filename as title tooltip on
// input field for better usability
$input.attr('title', filename)
},
focus: function () {
$input.trigger('focus')
}
})
$input
.on({
keyup: function () { $file.trigger('change') },
focus: function () { $file.trigger('change') },
blur: function () { $file.trigger('blur') },
// Open files when pressing [ENTER]
// on the input field
keydown: function (e) { if (e.which === 13) $file.trigger('click') }
})
// Append to DOM
$wrap.append($button, $input).insertAfter($file)
})
}
Here's a gist for ease of use: https://gist.github.com/3051209