I'm brand new to Svelte (3.0+)...and for my latest project, I'd like to emulate the functionality of many "todo" lists that allow you to edit todo items previously submitted by double-clicking on them (Here's an example of the functionality I'm looking for).
I imagine, the first step is figuring out how to make a div contentEditable with Svelte with the on:dblclick event handler. I'm having trouble figuring out the syntax for this task (though I can do it with vanilla javascript).
Here's the Svelte code I have so far: ( Here it is on CodeSandBox.io - see page: CEDiv.svelte)
<script>
function edit(event) {
//update db functionality goes here
//alert("you've 'submitted' your edit")
}
function handleDblClick() {
//I need help HERE...and probably on the div on:dblclick down below....
}
function handleKeydown() {
key = event.key;
keyCode = event.keyCode;
//submit the div's content to the edit function if enter or tab is pressed.
keyCode == 13 || keyCode == 9 ? edit(event) : null;
}
</script>
<style>
div.read-mode {
padding:10px;
border:1px solid green;
height:30px;
line-height:30px;
width:500px;
margin:0 auto;
}
div.edit-mode {
padding:10px;
background: lightgreen;
border:3px solid green;
height:26px;
line-height:26px;
width:496px;
margin:0 auto;
}
</style>
<div on:dblclick={handleDblClick} class="read-mode" on:keydown={handleKeydown} contentEditable="false">
I want this Div to be editable one double click.
</div>
Thanks in advance for your help!
Add a boolean variable
let editable = false;
which will be changed inside your handler
function handleDblClick(event) {
editable = true; // or use editable=!editable to toggle
}
bind your editable variable inside the attribute,
and take a look how to dynamically toggle the class "edit-mode" using a ternary operator
<div
on:dblclick={handleDblClick}
class={editable ? 'edit-mode': 'read-mode'}
on:keydown={handleKeydown}
contenteditable={editable}>
I want this Div to be editable on double click.
</div>
Here's a CodeSandbox fork
Related
In my NEXT.JS project, I want a simple <p> element to show below my button for x amount of time, and then I want it to disappear. I don't want to use alert() function. How can I manage that?
You can achieve that with a simple js event trigger. All you need to do is create a click event listener for the button, and then attach your 'p' element to the desired view.
Btw this has nothing to do with next.js this is core js DOM manipulation. You should learn about DOM and how to work with it, and then explore frameworks like next. Only with good knowledge of core js, and DOM manipulation, You will be able to really learn frameworks.
In case anyone needs it, here is the solution using useState and simple handler...
const [myAlert, setMyAlert] = useState(false)
const handleMyAlert = () => {
setMyAlert(true);
setTimeout(() => {
setMyAlert(false)
}, 5000);
}
You can use it like this: <button onClick={handleMyAlert}>Click me</button>
and for <p> set style={myAlert ? {display: "block"} : {display: "none}}
I'm too late but I post it anyway to try the snippet method.
function setMsg(e){
let inter = alertMsg(e);
}
function alertMsg(e){
document.getElementById("alertP").innerHTML="Click-me!";
document.getElementById("alertP").style.display="inline";
inter= setInterval("hideOutput()",3000);
}
function hideOutput(){
document.getElementById("alertP").style.display="none";
clearInterval(inter);
}
html, body{
font-family:"sans-serif";
font-size:1em;
}
button{
font-size:1.2em;
}
#buttonContainer{
width:200px;
margin:auto;
}
#alertP{
display:none;
text-align: center;
border:1px solid #000000;
background-color:red;
color:#FFFFFF;
}
<div id="buttonContainer">
<button id="clickableArea" onMouseOver="setMsg()">Alert Button</button>
<p id="alertP"></p>
</div>
I have a web page that can display several html style dialogs. I'd like to have code in just one place that can sense that the enter key was pressed and call the same method as would be called by clicking the OK button for the dialog. This allows visitor to press enter in the dialog to close it instead of having to click the OK button. Pretty simple stuff.
I'd to use jQuery to create a global keydown handler that looks for the enter key and calls the vuejs dlogClose method. I have created a minimal version of this below. In real life there would be multiple dialogs and more complex code to find the appropriate dlogClose method for the particular dialog. But I have removed that logic to create a minimal code example of where Vue is not behaving as expected.
Click the button to open the dialog. Then click the ok button. It will display an alert saying "dlogClose called" and then it will close the dialog. Works exactly as expected.
Then click the button to open the dialog again. This time press the enter key. The jQuery global event handler will see the enter key and call the same dlogClose method. That method will display an alert saying "dlogClose called" as expected BUT it's won't close the dialog! What? That's totally unexpected behaviour.
Can you explain why this behavior is valid? Or is this some sort of vue library bug?
var app = new Vue({
el: '#app',
data: {
dlogVisible: false
},
methods: {
dlogClose: function () {
alert("dlogClose called");
app.dlogVisible = false;
}
},
created: function () {
$(document).keypress(function (e) {
if (e.which == 13) {
app.dlogClose();
}
});
}
});
.dlog{
height:200px; width:200px; border: solid 1px gray;
padding: 20px; box-shadow: 0px 3px 15px gray;
position: absolute; top:40px; left:40px; background-color:white
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://unpkg.com/vue#2.2.5/dist/vue.min.js"></script>
<div>Global Enter Key Example</div>
<br />
<div id="app">
<div class="dlog" v-if="dlogVisible">
Pressing enter calls same method as clicking OK button<br />
<br />
<button type="button" v-on:click="dlogClose()">OK</button>
</div>
<button type="button" v-on:click="dlogVisible=true;">Open Dialog</button>
</div>
The issue here is that pressing the enter key while the dialog is open also triggers the open dialog button. So in essence, the dialog is closed and the immediately re-opened. Use preventDefault to stop that from happening.
$(document).keypress(function (e) {
e.preventDefault()
if (e.which == 13) {
app.dlogClose();
}
});
Here is the code updated.
var app = new Vue({
el: '#app',
data: {
dlogVisible: false
},
methods: {
dlogClose: function () {
alert("dlogClose called");
app.dlogVisible = false;
}
},
created: function () {
$(document).keypress(function (e) {
e.preventDefault()
if (e.which == 13) {
app.dlogClose();
}
});
}
});
.dlog{
height:200px; width:200px; border: solid 1px gray;
padding: 20px; box-shadow: 0px 3px 15px gray;
position: absolute; top:40px; left:40px; background-color:white
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://unpkg.com/vue#2.2.5/dist/vue.min.js"></script>
<div>Global Enter Key Example</div>
<br />
<div id="app">
<div class="dlog" v-if="dlogVisible">
Pressing enter calls same method as clicking OK button<br />
<br />
<button type="button" v-on:click="dlogClose()">OK</button>
</div>
<button type="button" v-on:click="dlogVisible=true;">Open Dialog</button>
</div>
You could create a jQuery event listener in the created () method within the top most page component (the App component). In that top level app component, make a method to to fire each time the enter button is hit of whenever it meets the condition you specify. For simplicity, you can then increment a vuex state counter or something. Whatever child components you have can watch for the counter to change and react accordingly (again based on conditions you set). I am not very knowledgeable about dispatching but that might be another route to investigate.
I'm creating a bookmarklet that displays some information on the first element you click on after running the bookmarklet. I would love to have it so the element you are hovering over has an outline, but only before you click. After you have selected an element, the outline would no longer appear when hovering (except if it already did so before my bookmarklet).
To get the clicked element, this works fine for me:
function getClickedElement(e) {
document.removeEventListener("click", getClickedElement);
e.preventDefault();
clickedElement = e.target || e.srcElement;
// Some code that displays information on clickedElement...
}
document.addEventListener("click", getClickedElement);
But I don't know how to do the CSS. It would work like all elements gain this CSS:
:hover {
outline: 1px solid black;
}
while selecting an element, but that stops once an element has been selected. Hope that all made sense.
Small example with the principle explained!
If the user clicks on the element, add a specific class.
CSS Rule adds outline-border only if the element does not match the selector inside the :not pseudo class!
document.addEventListener('click', function(evt) {
var target = evt.target || evt.source;
if(!target.classList.contains('element')) return;
if(target.classList.contains('selected'))
target.classList.remove('selected');
else
target.classList.add('selected');
}, true);
div.element {
width:100px;
height:100px;
background-color:silver;
display:inline-block;
}
.element.selected {
background-color:black;
}
.element:not(.selected):hover {
outline: 1px solid red;
}
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
I'm working on a Facebook reaction bar so it is pretty hard to copy the code here because it has a lot of events binded but all of you got facebook so if you want to check it by yourself - please do it.
The thing is that I managed to move the reaction bar under the react root and now I wanted to make the clicked reaction counter change the background color of itself to green.
And everything is working almost good excluding one thing: it is one click behind. To make you understand better I recorded little example how it looks. The red pulse ring appears when I click: https://vid.me/HqYp
Here is the changing code:
$(this).find('div._iu-[role="toolbar"]').bind('click',function(){
$(this).find('p.counter').each(function(){$(this).css('background-color','#48649F');});
$(this).find('span[aria-pressed="true"]').find('p.counter').css('background-color','green');
});
$(this) is div[id*="post"] so in $(this) I'm getting div with the whole post.
I thought that maybe I should use a callback function after changing-every-counter-to-default-color function but I don't know am I right and if it's right solution.
Thanks from above. (:
You can probably simplify this a bit. Although without the html structure I can't know for sure how the layout of the function works with respect to the event origin. Also I am not sure when the aria-pressed is set to true so I made the function a bit more generic. You simply add a data attribute to target the span you want to be targeted by the click.
<div class="_lu-" role="toolbar" data-target=".facebook-counter">
Later in your javascript you do the following
var $t = $(this);
var $t.target = $(this).data('target');
$t.on('click','div._lu-[role="toolbar"]', function() {
$t.find($t.target).css({
'background-color':'green'
}).siblings().css({'background-color','#48649F'});
});
This code is assuming first that your spans are in the same container, and second that the first $(this) refers to the parent container of this whole toolbar, and last that you have put data-target="" attributes with selectors for the appropriate target you want to affect.
This is a sample:
$(document).ready(function(){
$('.toolbar').on('click','.toolbar-item .icon', function(event) {
event.preventDefault();
event.stopPropagation();
if(!this.$) this.$ = $(this);
if(!this.parent) this.parent = this.$.parent();
if(!this.counter) this.counter = this.$.siblings('.counter');
this.parent.addClass('selected').siblings('.selected').removeClass('selected');
var count = this.counter.data('value');
count++;
this.counter.data('value',count);
this.counter.html(count);
});
});
.toolbar {
font-size:0;
text-align:center;
}
.toolbar-item .icon {
background:#FFF;
padding:30px;
border:1px solid #AAA;
border-radius:100%;
margin:0 20%;
transition:0.8s ease all;
}
.selected .icon {
background:#369;
}
.toolbar-item .counter {
background:#E0E0E0;
margin:0 10px;
transition:0.4s ease background;
}
.selected .counter {
background:#509050;
}
.toolbar-item {
font-size:10pt;
width:25%;
display:inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="toolbar">
<div class="toolbar-item">
<div class="icon">Like</div>
<div class="counter" data-value="0">0</div>
</div>
<div class="toolbar-item">
<div class="icon">Wow</div>
<div class="counter" data-value="0">0</div>
</div>
<div class="toolbar-item">
<div class="icon">Sad</div>
<div class="counter" data-value="0">0</div>
</div>
<div class="toolbar-item">
<div class="icon">Angry</div>
<div class="counter" data-value="0">0</div>
</div>
</div>
As of jQuery 1.7 they introduced the .on('click', function().... method. Try that instead and see if you get the same results.
Quick answer without having tested or the time to test your code. I recently had a performance issue with a nested function, so maybe look at that second line with the .each() method.
I've got code to show/hide two divs based upon the clicking of an arrow. However, the second div requires two clicks before it hides and the arrow doesn't change as expected. The top div works perfectly. Any suggestions about what I'm doing wrong, please?
EDIT - Thanks to the two posters who have pointed out my naming error in the arrows. However, after loading the page, the second div still requires two clicks before it toggles.
HTML
<div id="start_conditions_arrow" class="arrow_down" onclick="toggleDiv('start_conditions')"></div>
<h2>Starting Conditions</h2>
<div id="start_conditions">
<%= render :partial => 'start_conditions', :object => #page.start_conditions %>
</div>
<div id="probability_arrow" class="arrow_right" onclick="toggleDiv('probability_inputs')"></div>
<h2>Probability Inputs</h2>
<div id="probability_inputs">
<%= render :partial => 'probability_inputs', :object => #page.probability_inputs %>
</div>
Javascript
var toggleDiv = function(id){
var tag = document.getElementById(id).style;
if(tag.display == 'none'){
document.getElementById(id).style.display='block';
document.getElementById(id + '_arrow').className='arrow_down';
} else {
document.getElementById(id).style.display='none';
document.getElementById(id + '_arrow').className='arrow_right';
}
};
CSS
.arrow_down, .arrow_right {
width: 0;
height: 0;
margin: 12px 12px 0 0;
float: left;
cursor: pointer;
}
.arrow_down {
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 18px solid #d5d5d5;
}
.arrow_right {
border-top: 10px solid transparent;
border-bottom: 10px solid transparent;
border-left: 18px solid #d5d5d5;
}
#probability_inputs {
display: none;
}
#start_conditions {
display: none;
}
Here's a Codepen including the above: https://codepen.io/anon/pen/yXozYJ
The second arrow element should have probability_inputs_arrow as id and not probability_arrow, as you're building its id in the function as id + '_arrow' and you pass 'probability_inputs'.
I know that I'm a little late answering this, but I have a solution. There are two issues that should be addressed and they're as follows:
The ID of the last div is incorrect as stated in PSR's answer.
The check against the style attribute of the element in the toggleDiv method doesn't account for the lacking of the style attribute upon page load.
Regarding the first issue, the ID should read probability_inputs_arrow, which fixes the issue with the arrow not being updated when it is toggled. However, the second issue needs a little more explaining...
Upon page load, the CSS provided is applied to the elements with the IDs probability_inputs and start_conditions, giving them both a display value of none. Upon clicking on either one of these arrows, you're retrieving the style attributes of the targeted element, and checking the display property which upon page load is empty. This is why it requires two clicks to achieve the desired behaviour, because the first click is effectively just setting that value in the else statement.
Here's a link to an updated Codepen that contains the fixes: https://codepen.io/anon/pen/oweELy
actually you are getting error here
The second element need to have have probability_inputs_arrow as id and not probability_arrow,
The style.display is set to "" by default when the page loads for the first time, thus it ends up in the else block and then requires a second click to go to the if block. Just add this to your else if statement.
<script>
var x = document.getElementById("myDIV");
if (x.style.display == "none")
{
x.style.display = "block";
}
else if((x.style.display == 'null')||(x.style.display == ""))
{
x.style.display = "block";
}
else
{
x.style.display = "none";
}
</script>
I had this problem and it was due to the default state of the DIV. Even if you set the DIV class value is set display: none, it appears some browser do not accept this state, and therefore it is unknown.
To solve the problem, using your own code, add another condition of default:
var toggleDiv = function(id){
var tag = document.getElementById(id).style;
if(tag.display == 'none')
{
document.getElementById(id).style.display='block';
document.getElementById(id + '_arrow').className='arrow_down';
}
else if(tag.display == 'block')
{
document.getElementById(id).style.display='none';
document.getElementById(id + '_arrow').className='arrow_right';
}
else
{
document.getElementById(id).style.display='block';
document.getElementById(id + '_arrow').className='arrow_down';
}
};
`