scrollLeft setter is not working (Angular, jqLitle) - angularjs

I have this code
var input = element.find('input').eq(0);
scope.focus = function() {
if (!input.length) return;
input[0].focus();
input[0].select();
var divWrapper = input.parent('.tags')[0];
divWrapper.scrollLeft = divWrapper.scrollWidth;
console.log(divWrapper.scrollWidth);
console.log(divWrapper.scrollLeft);
};
This is inside a directive fnLink and focus is called when ui is changed this because use overflow: hidden and input disapear when many items on left... so I need to scroll to far right and keep input visible.
The
divWrapper.scrollLeft = divWrapper.scrollWidth;
works fine on chrome dev tools but when focus is called don't update the scrollLeft attribute.

Related

Reactjs web app - MutationObserver use in Safari browser not as expected

I have a react webapp that renders 2 text areas which are resize-vertical (using CSS). My requirement is that when one text area is resized by the user, the code captures it and apply the height to the other text area.
Based on my research, currently, there isn't a convenient method like textarea.onResize(). So, I am using MutationObserver. The API is very elegant and exactly what I need. I was able to configure it properly, capture the change in style attribute and apply it to other text area. This works fine in Google Chrome (Version 76.0.3809.132 (Official Build) (64-bit)) and Mozilla FireFox (Version 68.0.1esr (64-bit)). All this testing was done on Mac OS High Sierra (Version 10.13.6).
The problem
Although, when it comes to Safari browser (Version 12.1.2 (13607.3.10)), it doesn't seem to work as expected.
Upon debugging, I found out that when user resize a given text area, style attribute gets appended to it, with some assigned height and for each change, MutationObserver observes the change produces a MutationRecord, which I capture in my callback to take appropriate actions (in this case, apply height to other text area). Although, Safari browser does not produce MutationRecord for change in style attribute when text area is resized by the user.
Two things:
If I inspect the text area in developer mode of the browser and make a change to the style attribute of the element, MutationRecord is produced correctly and my callback does indeed execute, which is very weird.
I created an example in JS Fiddle, where, in Safari if you resize the text area, no MutationRecord is produced and you won't see any alerts but if you inspect and make a change manually, you will see the alert (appropriate MutationRecords are produced). In Chrome or Firefox, resizing the text area will produce the window alert without needing to make changes by inspecting the element.
Code
CSS
.textField textarea {
resize: vertical;
}
componentDidMount()
componentDidMount() {
if ('MutationObserver' in window) {
const config = {
attributes: true,
subtree: true,
attributeFilter: ['style'],
attributeOldValue: true
};
const sourceTextArea = window.getElementById('sourceTextArea');
this.sourceObserver = new MutationObserver(this.handleManualSourceTextAreaResize);
this.sourceObserver.observe(sourceTextArea, config);
const targetTextArea = window.getElementById('targetTextArea');
this.targetObserver = new MutationObserver(this.handleManualTargetTextAreaResize);
this.targetObserver.observe(targetTextArea, config);
}
}
Callback for source text area
handleManualSourceTextAreaResize = (mutationsList, observer) => {
const sourceTextArea = getElement('sourceTextArea');
const targetTextArea = getElement('targetTextArea').children[0];
if (!this.isStopPropagation) {
targetTextArea.setAttribute('style', 'height: ' + sourceTextArea.offsetHeight + 'px');
this.isStopPropagation = true;
} else {
this.isStopPropagation = false;
}
}
Callback for target text area
handleManualTargetTextAreaResize = (mutationsList, observer) => {
const sourceTextArea = getElement('sourceTextArea').children[0];
const targetTextArea = getElement('targetTextArea');
if (!this.isStopPropagation) {
sourceTextArea.setAttribute('style', 'height: ' + targetTextArea.offsetHeight + 'px'');
this.isStopPropagation = true;
} else {
this.isStopPropagation = false;
}
}

AngularJS Get text selection and append html to start and end of selection

Have little dilemma here. I'm building text editor in angular js. The problem that I have is, when user selects part of text within a paragraph or heading I need to change styling of that part of text to bold / italic etc.
So basically I need to wrap selected text in <strong></strong> or <em></em>.
Plunker
I have a directive
editorApp.directive('watchSelection', function() {
return function(scope, elem) {
elem.on('mouseup', function() {
scope.startPosition = elem[0].selectionStart;
scope.endPosition = elem[0].selectionEnd;
// scope.selected = elem[0].value.substring(start, end);
scope.$apply();
});
};
});
That gets text selection its startposition and endposition. On button click I need to wrap that selection in specific tags, which I'm hoping to accomplish with this function:
$scope.boldText = function(startPosition, endPosition) {
$scope.start = startPosition;
$scope.end = endPosition;
var htmlStart = angular.element('<strong>');
var htmlEnd = angular.element('</strong>');
$scope.start.append(htmlStart);
$scope.end.append(htmlEnd);
};
I relatively new to angular and I might have taken a bigger bite than I can handle :)
Issue is I can't get selection to wrap inside them tags.
You don't need to watch anything.
$scope.boldText = function() {
document.execCommand('bold');
};
This will bold the selected text.

Prevent angular-ui ui-scroll from loading in hidden div

I have an angular-ui ui-scroll directive (1.3.0) living in a div that is hidden by default with an ng-show. When it is hidden, ui-scroll just keeps loading data from the datasource even though the ui-scroll-viewport's height is set. If the div is shown, it behaves correctly. So, for now my div is 1px wide :/
I'm sure I can solve this with an ng-if to dynamically add this to the DOM. However, I wanted the div to hide/show responsively - so driven off of css.
Any suggestions on how to have ui-scroll only load 1 page from the buffer when hidden? thank you.
You can try to manage it through the datasource implementation:
$scope.datasource.get = function(index, count, success) {
// prevent data getting while ui-scroll is invisible
if (!$scope.isScrollerVisible) {
return; // or let him request for 1 pack and prevent others, any logic
}
// get required data and pass it into success callback
var result = [];
for (var i = index; i <= index + count - 1; i++) {
result.push({text: "item #" + i});
}
success(result);
}
Also you probably need to initiate scroll reloading once (adapter doc):
$scope.$watch('isScrollerVisible', function(value) {
if(value) {
$scope.datasourceAdapter.reload()
}
});

Bind data-opacity attribute to scope variable

There's a jquery.minicolors pluggin, and there is a angularified version of this plugin that I wish use.
The usage is like so:
On page open data for color picker (hex color and opacity) is retrieved from server.
Then that values is used to "build" colorpicker (so that its color and opacity are equal to values received from server).
Then user may click on picker and change color and opacity. Results must be reflected in corresponding $scope variables.
I have binded color hex value via ng-model and it works like a charm, but I have troubles with opacity. I have tried to use ng-attr- for data-opacity attribute, but when I change manually opacity, results are not "sent" back to scope variable..
<input minicolors="options" ng-model="thecolor" ng-attr-data-opacity="{{opacity}}">
To demonstrate this I have created a plunker demo with description.
The problem is that the input field only binds to color part. If you add a ng-change to your input you will see that this is only fired if you change the color but not when opacity is changed.
You should move your code into a directive. Here you can use the "change" eventHandler from minicolors as describedin another question Minicolors AngularJS and the docs
UPDATE
If you take a look at the code
http://kaihenzler.github.io/angular-minicolors/angular-minicolors.js
It seems like the change event is there but only binds the hex value.
var settings = getSettings();
settings.change = function (hex) {
scope.$apply(function () {
ngModel.$setViewValue(hex);
});
};
I am guessing it should be.
var settings = getSettings();
settings.change = function (hex,opacity) {
scope.$apply(function () {
ngModel.$setViewValue(hex);
});
};
But the opacity is not passed into any value. Seems like a bug in the directive. If you use a console.log(opacity) you can see the value change.
Update 2.
Simple fix for you
settings.change = function (hex,opacity) {
scope.opacity = opacity
scope.$apply(function () {
ngModel.$setViewValue(hex);
});
};
After many attempts I have managed to modify source code to be able to two-way-bind opacity value to variable I desire. Here's a modified plunker.
What I've added:
First, we modify the tag by passing in additional watch-opacity="varname" attribute to define what variable should hold the initial opacity value. This variable will also be changed when we change the opacity slider and viceversa - the opacity slider will change when new data is written into this variable (ex. data received from server).
<input minicolors="options" ng-model="thecolor" data-opacity="" watch-opacity="settings.somewhat.opacity">
Then we get the opacity value from our settings and ask minicolors to use it as opacity for now, or use 1.0 if the variable have no value yet.
var opacity = scope.$eval(attrs.watchOpacity) || "1.0";
element.minicolors('opacity', opacity);
And then we modify our onchange function so that our settings variable for opacity is being updated also when opacity is changed due to opacity slider change:
var settings = getSettings();
settings.change = function (hex, opacity) { // <- fix!
scope.$apply(function () {
ngModel.$setViewValue(hex);
$parse(attrs.watchOpacity).assign(scope, opacity); // <- fix!
});
};
That's it! This way we can define variable for each colorpicker that will hold opacity value and bind any opacity changes via slider to it.
You can use an attribute watch:
(edited)
// Template
<input minicolors="options" ng-model="thecolor">
// Controller:
$("input[minicolors]").each(function() {
var $self = $(this);
$scope.$watch(
function() {
return $self.attr("data-opacity");
},
function(opacity) {
$scope.opacity[ $self.index() ] = opacity;
});
});

Sencha Touch 2.0 - How to set scrolling inside a textarea for Mobile Safari?

In my mobile safari project, i need to create a message posting feature. it is requires scrolling inside a textarea when lines of texts exceed the max rows of the text area. i couldn't find 'scrollable' property in Ext.field.textarea, any idea how?
Cheers!
There is a bug in touch 2.0.x such that the framework explicitly prevents the scroll action. Supposedly a fix will be in 2.1, though I didn't see that officially, just from a guy on a forum.
Until then, there is kind of a solution for touch1 here http://www.sencha.com/forum/showthread.php?180207-TextArea-scroll-on-iOS-not-working that you can port to V2. It basically involves adding an eventlistener to the actual textarea field (not the sencha object) and then calling preventdefault if it's a valid scrollevent.
The full code is at that link, but the salient bits are here.
Grab the <textarea> field (not the Sencha Touch object) directly and use addListener to apply
'handleTouch' on touchstart and 'handleMove' on touchmove
handleTouch: function(e) {
this.lastY = e.pageY;
},
handleMove: function(e) {
var textArea = e.target;
var top = textArea.scrollTop <= 0;
var bottom = textArea.scrollTop + textArea.clientHeight >= textArea.scrollHeight;
var up = e.pageY > this.lastY;
var down = e.pageY < this.lastY;
this.lastY = e.pageY;
// default (mobile safari) action when dragging past the top or bottom of a scrollable
// textarea is to scroll the containing div, so prevent that.
if((top && up) || (bottom && down)) {
e.preventDefault();
e.stopPropagation(); // this tops scroll going to parent
}
// Sencha disables textarea scrolling on iOS by default,
// so stop propagating the event to delegate to iOS.
if(!(top && bottom)) {
e.stopPropagation(); // this tops scroll going to parent
}
}
Ext.define('Aspen.util.TextArea', {
override: 'Ext.form.TextArea',
adjustHeight: Ext.Function.createBuffered(function (textarea) {
var textAreaEl = textarea.getComponent().input;
if (textAreaEl) {
textAreaEl.dom.style.height = 'auto';
textAreaEl.dom.style.height = textAreaEl.dom.scrollHeight + "px";
}
}, 200, this),
constructor: function () {
this.callParent(arguments);
this.on({
scope: this,
keyup: function (textarea) {
textarea.adjustHeight(textarea);
},
change: function (textarea, newValue) {
textarea.adjustHeight(textarea);
}
});
}
});

Resources