#javascript #css #google-chrome #google-chrome-extension
#javascript #css #google-chrome #google-chrome-расширение
Вопрос:
Я разрабатываю расширение Chrome для быстрого доступа к билетам JIRA. Таблица с текстовыми элементами «случайным образом» очень медленно реагирует на события ввода / страницы, такие как on hover
, focus
, и text input
, иногда для ответа требуется по крайней мере НЕСКОЛЬКО секунд.
Я бы предпочел не выпускать это, не понимая, что происходит, и масштабы воздействия. Я действительно просто хочу понять, что происходит.
Это происходит ТОЛЬКО тогда, когда я использую внешний дисплей (MacBook Pro 13 дюймов, 2017, 8 ГБ через USB-C концентратор).
Я заметил: 1) Тот же код на более мощных машинах работает нормально, используя те же периферийные устройства 2) Все работает нормально, когда я перетаскиваю окно браузера на дисплей моего ноутбука 3) Это происходит, по-видимому, случайным образом в зависимости от позиции ввода во всплывающем окне. Изменение размера верхнего и нижнего полей часто может помочь либо решить проблему, либо переместить ее в другую строку.
Я создал функцию для изменения размера верхнего и нижнего полей в зависимости от количества элементов, но это только смягчает проблему, а не полностью ее решает.
FWIW, не похоже, что используется ужасный объем памяти, и система обнаруживает входные данные, поскольку она замечает ключевые события. Текстовые поля с проблемами не занимают больше места на процессоре / памяти, чем те, которые этого не делают.
popup.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<link href="https://fonts.googleapis.com/css?family=IBM Plex Mono:400,400i|IBM Plex Sans Condensed:400,400i|IBM Plex Sans:100,100i,400,400i,700,700i|IBM Plex Serif:400,400i" rel="stylesheet">
<link rel="stylesheet" href="popup-style.css">
<style type="text/css"> </style>
<meta charset="utf-8">
<title>Quick Ticket For JIRA</title>
</head>
<body>
<div class="NewJIRAForm">
<div class="header">
<h1>QUICK TICKET FOR JIRA</h1>
<div class="QuickJIRAcontrols">
<input type="button" id='optionsButton' value="Settings" class="controlButtons">
<input type="button" id='toggleAddNewButton' value="Add New" class="controlButtons">
</div>
</div>
<div class="addNewForm" id="addNewForm">
<div class="addFirstRowForm">
<input type="text" name="newJIRABoardName" placeholder="Name of your New JIRA board" class="newJIRABoardNameInputField" id="newJIRABoardName">
<input type="text" name="newJIRABoardKey" placeholder="Project Key" class="newJIRABoardKeyInputField" id="newJIRAProjectKey" >
</div>
<div class="addSecondRowForm">
<input type="text" name="newJIRABaseURL" placeholder="Base URL of your JIRA board" class="newJIRABaseURLField" id='newJIRABaseURL'>
<input type="button" id='addNewButton' value="Add">
</div>
</div>
</div>
<div id='CurrentQuickJIRAs' class="CurrentQuickJIRAs">
</div>
</body>
<script src="popup.js"></script>
</html>
popup.js
document.addEventListener('DOMContentLoaded', documentEvents , false);
// using local array to avoice async issues;
var localQuickJiraArray = [];
var addNewQuickJIRAIsShown;
var currentNewJIRABoardNameTextInput;
var currentNewJIRABoardURLTextInput;
var currentNewJIRABoardProjectKeyTextInput;
function documentEvents() {
document.getElementById('addNewButton').addEventListener('click',
function() { addNewQuickJIRA(document.getElementById('newJIRABaseURL') , document.getElementById('newJIRABoardName'), document.getElementById('newJIRAProjectKey'));
});
document.getElementById('toggleAddNewButton').addEventListener('click',
function() { toggleAddNewQuickJIRA();
});
document.getElementById('optionsButton').addEventListener('click',
function() { chrome.tabs.create({'url': "/options.html" } );
});
document.getElementById("newJIRABoardName").addEventListener("keyup", saveNewFormData);
document.getElementById("newJIRAProjectKey").addEventListener("keyup", saveNewFormData);
document.getElementById("newJIRABaseURL").addEventListener("keyup", saveNewFormData);
//sync local array of QuickJIRAs with Chrome Storage
chrome.storage.sync.get(function(data) {
if (Object.keys(data).length > 0 amp;amp; data.QuickJIRAs ) {
localQuickJiraArray = data.QuickJIRAs;
}
chrome.storage.sync.set(data, function() {
drawCurrentQuickJIRATable();
// logStorage();
});
});
}
function saveNewFormData() {
currentNewJIRABoardNameTextInput = document.getElementById("newJIRABoardName").value;
currentNewJIRABoardURLTextInput = document.getElementById("newJIRABaseURL").value;
currentNewJIRABoardProjectKeyTextInput = document.getElementById("newJIRAProjectKey").value;
if ( currentNewJIRABoardNameTextInput == "" amp;amp; currentNewJIRABoardURLTextInput == "" amp;amp; currentNewJIRABoardProjectKeyTextInput == "" ) {
isDataToPersist = false;
} else {
isDataToPersist = true;
}
chrome.runtime.sendMessage({
currentNewJIRABoardNameTextInput: currentNewJIRABoardNameTextInput,
currentNewJIRABoardURLTextInput: currentNewJIRABoardURLTextInput,
currentNewJIRABoardProjectKeyTextInput: currentNewJIRABoardProjectKeyTextInput,
isDataToPersist: isDataToPersist
},
function(response) {
if (response == "saved") {
} else {
throw new Error("Error Persisting data while adding Quick Ticket");
}
}
);
}
function drawCurrentQuickJIRATable() {
chrome.storage.sync.get(function(data){
// logStorage();
if (Object.keys(data).length> 0 amp;amp; data.QuickJIRAs != undefined amp;amp; data.QuickJIRAs.length > 0) {
// Clear current jiras
document.getElementById('CurrentQuickJIRAs').innerHTML = '' ;
//Draw New Table
//**could improve by just adding the last one and not clearing
for (var i = 0; i < data.QuickJIRAs.length; i ) {
var JIRABoardID = data.QuickJIRAs[i].JIRABoardName;
var JIRABoardBaseURL = data.QuickJIRAs[i].JIRABaseURL;
document.getElementById('CurrentQuickJIRAs').innerHTML = '<div class="quickJIRARow">'
'<a class="goNewLink" href="' data.QuickJIRAs[i].JIRABaseURL "/" data.QuickJIRAs[i].JIRAProjectKey ' "> ' data.QuickJIRAs[i].JIRABoardName '</a>'
'<input type="text" placeholder="Ticket Number" id="inputFieldBoardID_' i '" ' ' class="goToInput">'
'<div class="quickJIRARowButtons">'
'<button type="button" id="goNewButton_' i '" ' ' class="goNewButton">New Tab</button>'
'<button type="button" id="goCurrentButton_' i '" ' ' class="goCurrentButton">Current Tab</button>'
'<button type="button" id="removeButton_' i '" ' ' class="removeButton">Remove</button>'
'</div>'
'</div>' ;
}
//add click function to link;
var jiraBoardLinks = document.getElementsByTagName("a");
for (var i = 0; i < jiraBoardLinks.length; i ) {
(function () {
var link = jiraBoardLinks[i];
var url = link.href;
link.onclick = function () {
chrome.tabs.create({active: true, url: url});
};
})();
}
// Set Go New Button Click Event
var goButtonArray = document.getElementsByClassName('goNewButton');
for (var x = 0; x < goButtonArray.length; x ) {
var elementID = "goNewButton_" String(x);
document.getElementById(elementID).addEventListener('click', function () {
var newTab = true;
navigateToQuickJIRA(this, newTab);
});
}
// Set Go Current Button Click Event
var goButtonArray = document.getElementsByClassName('goCurrentButton');
for (var x = 0; x < goButtonArray.length; x ) {
var elementID = "goCurrentButton_" String(x);
document.getElementById(elementID).addEventListener('click', function () {
var newTab = false;
navigateToQuickJIRA(this, newTab);
});
}
// Set Go Remove Button Click Event
var goButtonArray = document.getElementsByClassName('removeButton');
for (var x = 0; x < goButtonArray.length; x ) {
var elementID = "removeButton_" String(x);
document.getElementById(elementID).addEventListener('click', function () {
removeJIRA(this);
});
}
// Set Go To JIRA in current Tab on Return and
var goToInputArray = document.getElementsByClassName('goToInput');
for (var x = 0; x < goToInputArray.length; x ) {
var elementID = "inputFieldBoardID_" String(x);
document.getElementById(elementID).addEventListener("keyup", function (event) {
if (event.key === "Enter") {
var newTab = false;
navigateToQuickJIRA(this, newTab);
this.value = "";
}
});
}
//To-Do: Add Options to Keep Add Window open
toggleAddNewQuickJIRA(false);
} else {
document.getElementById('CurrentQuickJIRAs').innerHTML = '' ;
toggleAddNewQuickJIRA(true);
}
checkIfExistingAddNewFields()
});
}
function handleInputFromGoToInput(textfield) {
}
//Check to see if user is mid adding a new JIRA
function checkIfExistingAddNewFields() {
// Send message to background to get current input values if user was mid input and closed the popup
chrome.runtime.sendMessage({
checkingForData : true
},
function(response) {
if (response.currentNewJIRABoardNameTextInput != undefined amp;amp; response.currentNewJIRABoardURLTextInput != undefined amp;amp; response.currentNewJIRABoardProjectKeyTextInput != undefined) {
document.getElementById("newJIRABoardName").value = response.currentNewJIRABoardNameTextInput;
document.getElementById("newJIRABaseURL").value = response.currentNewJIRABoardURLTextInput;
document.getElementById("newJIRAProjectKey").value = response.currentNewJIRABoardProjectKeyTextInput;
toggleAddNewQuickJIRA(true);
}
}
);
}
function navigateToQuickJIRA (button, newTab) {
// Get the value of button Id to use to map to coresponding textfield
var idValue = String(button.id).split("_").pop();
var goToInputId = "inputFieldBoardID_" idValue;
var goToInputIdValue = document.getElementById(goToInputId).value;
//Get base URL value from Storage
// ** Change to local Array
var projectKey = localQuickJiraArray[idValue].JIRAProjectKey;
var goToInputIdValue= validateTicketNumberInput(goToInputIdValue, projectKey);
chrome.storage.sync.get(function(data){
if(chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
} else {
console.log(data);
var jiraBoardID = data.QuickJIRAs[idValue].JIRABaseURL;
var jiraURL = jiraBoardID "/" goToInputIdValue;
//Need function to avoid race-condition, could probably use callback in the future
goToInputIdValue.value = "";
gotoNewURL(jiraURL, newTab);
}
});
}
function validateTicketNumberInput(goToInputIdValue, projectKey){
if (goToInputIdValue.length > projectKey.length) {
var inputToCheckForProjectKey = goToInputIdValue.substr(0, projectKey.length 1);
}
//Check if input is just numbers - if yes, add the project key to the front and dash - it's valid
//Check if input is contains valid project key in the right location with a dash - if yes - it's valid
//else - not a valid input - throw error
if (/^d $/.test(goToInputIdValue)) {
var sanitizedInput = projectKey "-" goToInputIdValue;
return sanitizedInput;
} else if (inputToCheckForProjectKey !== 'undefined' amp;amp; inputToCheckForProjectKey == projectKey "-") {
return goToInputIdValue;
} else {
alert("It looks like your ticket number isn't valid for this Project. Please enter a valid ticket number.");
throw new Error("Invald Ticket Number Entered");
}
}
function gotoNewURL(jiraURL, newTab) {
if (newTab == false) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var tab = tabs[0];
chrome.tabs.update(tab.id, { url : jiraURL });
});
} else {
chrome.tabs.create({ url : jiraURL });
}
}
function removeJIRA(button) {
var doesWantToDelete = confirm("Do you really want to Delete that link?");
if (doesWantToDelete == true ) {
var idValue = String(button.id).split("_").pop();
//Get base URL value from Storage
chrome.storage.sync.get(function(data){
if(chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
} else {
data.QuickJIRAs.splice(idValue, 1);
localQuickJiraArray.splice(idValue, 1);
}
chrome.storage.sync.set(data, function() {
//Race condition - use callback in the future
drawCurrentQuickJIRATable();
});
});
}
}
function addNewQuickJIRA(baseURLInput , boardNameInput, projectKeyInput) {
if (baseURLInput.value == "" || boardNameInput.value == "" || projectKeyInput.value == "") {
alert("Looks like some fields are missing. Please fill in all fields before adding a new Quick JIRA");
throw new Error("missing fields upon Quick JIRA submission");
}
var baseURL = baseURLInput.value.replace(//$/, '');
baseURL = validateBaseURL(baseURL);
var boardName = validateBoardName(boardNameInput.value.trim());
var projectKey = validateProjectKey(projectKeyInput.value.trim());
var doesExist = checkIfInputDataDoesExist(baseURL, boardName, projectKey);
if (doesExist == false) {
chrome.storage.sync.get(function(data) {
var objectToAdd = {'JIRABaseURL' : baseURL , 'JIRABoardName' : boardName , 'JIRAProjectKey' : projectKey};
if (Object.keys(data).length > 0 amp;amp; data.QuickJIRAs ) {
data.QuickJIRAs.push(objectToAdd);
localQuickJiraArray.push(objectToAdd);
} else {
data.QuickJIRAs = [objectToAdd];
localQuickJiraArray = [objectToAdd];
}
baseURLInput.value = ""
boardNameInput.value = ""
projectKeyInput.value = ""
saveNewFormData();
chrome.storage.sync.set(data, function() {
drawCurrentQuickJIRATable();
// logStorage();
});
});
}
}
function checkIfInputDataDoesExist(baseURL, boardName, projectKey) {
if (localQuickJiraArray.length != 0) {
//check for existing baseURL and board name; set doesExist to true if one does
//Removed checking for Base URL's since it's possible there are multiple projects on the same URLs
for (var i = 0; i < localQuickJiraArray.length; i ) {
var existingJiraBoardID = localQuickJiraArray[i].JIRABoardName;
var existingJiraBoardBaseURL = localQuickJiraArray[i].JIRABaseURL;
var existingJiraProjectKey = localQuickJiraArray[i].JIRAProjectKey;
if (boardName == existingJiraBoardID) {
alert("Looks like you already a QuickJIRA with that Name. n n Please try adding a your Quick JIRA with a new name");
return Boolean(true);
} else if (projectKey == existingJiraProjectKey) {
alert("Looks like you already a QuickJIRA with that Project Key. n n Please try adding a your Quick JIRA with a newJIRAProjectKey");
return Boolean(true);
}else {
return Boolean(false);
}
}
} else {
return Boolean(false);
}
}
function validateBaseURL(baseURLInput) {
var regexQuery = "^(https?://)?(www\.)?([-a-z0-9]{1,63}\.)*?[a-z0-9][-a-z0-9]{0,61}[a-z0-9]\.[a-z]{2,6}(/[-\w@\ \.~#\?amp;/=%]*)?$";
var regexTest = new RegExp(regexQuery, 'i');
var isValidURL = regexTest.test(baseURLInput);
if (isValidURL == true) {
var arrayOfMatches = baseURLInput.match(/.*/(.*)$/);
if (arrayOfMatches.length < 2 || arrayOfMatches[1].toLowerCase() != "browse") {
var confirmURL = confirm("Are you sure you want to Add this URL? n n Quick Ticket works best when the last part of the URL is `/browse` !");
if (confirmURL == true) {
return baseURLInput;
} else {
throw new Error("User did not want to add this URL");
}
} else {
return baseURLInput;
}
} else {
alert("That doesn't look like valid JIRA URL. n n Please enter a valid URL n n URLs should not contain Project Keys");
throw new Error("invalid JIRA entered");
}
}
function validateBoardName(boardNameInput) {
if (boardNameInput.length < 50) {
var regexQuery = "^[A-Za-z0-9 _]*[A-Za-z0-9][A-Za-z0-9 _]*$";
var regexTest = new RegExp(regexQuery, 'i');
var isValidBoardNameInput = regexTest.test(boardNameInput);
if (isValidBoardNameInput == true) {
return boardNameInput;
} else {
alert("That doesn't look like valid Board Name. n n Project Keys may only contain letters and numbers");
throw new Error("invalid Board Name Add Attempt");
}
} else {
alert("That doesn't look like valid Board Name. n n Please enter a Board Name less than 50 characters");
throw new Error("invalid Board Name Add Attempt");
}
}
function validateProjectKey(projectKeyInput) {
if (projectKeyInput.length < 50) {
var regexQuery = "^[A-Z][A-Z_0-9] ";
var regexTest = new RegExp(regexQuery);
var isValidProjectKeyInput = regexTest.test(projectKeyInput);
if (isValidProjectKeyInput == true) {
return projectKeyInput;
} else {
alert("That doesn't look like valid Project Key. n n Project Keys may only contain Uppercase letters, numbers, and '_' ");
throw new Error("invalid Project Key Add Attempt");
}
} else {
alert("That doesn't look like valid Project Key. n n Please enter a Project Key less than 50 characters");
throw new Error("invalid Project Key Add Attempt");
}
}
function toggleAddNewQuickJIRA(shouldDisplay) {
var newFormDiv = document.getElementById("addNewForm");
var newFormButton = document.getElementById("toggleAddNewButton");
if (shouldDisplay == false) {
newFormDiv.style.display = "none";
addNewQuickJIRAIsShown = false;
newFormButton.value="Add New"
} else if (shouldDisplay == true) {
newFormDiv.style.display = "block";
addNewQuickJIRAIsShown = true;
newFormButton.value="Hide"
} else {
if (newFormDiv.style.display == "block") {
newFormDiv.style.display = "none";
addNewQuickJIRAIsShown = false;
newFormButton.value="Add New"
} else {
newFormDiv.style.display = "block";
addNewQuickJIRAIsShown = true;
newFormButton.value="Hide"
}
}
}
// Log Storage for debugging
function logStorage() {
if(chrome.storage) {
chrome.storage.local.get(function(data){
console.log("chrome.storage.local:");
if(chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
} else {
console.log(data);
}
chrome.storage.sync.get(function(data){
console.log("chrome.storage.sync:");
if(chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
} else {
console.log(data);
}
});
});
} else {
console.warn("chrome.storage is not accessible, check permissions");
}
}
всплывающее окно-css
h1 {
margin: 0px;
font-size: 12pt;
}
body {
width: 600px;
margin: 0px;
z-index: 1;
}
.header {
display: inline-block;
min-width: 570px;
}
.header h1{
margin: 0;
display: inline-block;
float: left;
}
.header .QuickJIRAcontrols {
float: right;
width: 275px;
margin-right: -5px;
text-align: center;
}
#toggleAddNewButton, #optionsButton {
display: inline-block;
margin: 0;
padding: 0;
height: 19px;
width: 130px;
}
#optionsButton {
float: left;
color: black;
}
#optionsButton:hover {
background-color: #7B7B7B;
color: #FFFFFF;
}
#toggleAddNewButton {
float: right;
}
.addNewForm {
display: block;
}
.NewJIRAForm {
min-width: 100%;
background-color: #D8D8D8;
padding: 15px;
padding-top: 20px;
}
.NewJIRAForm input[type=text] {
width: 375px;
}
.NewJIRAForm input[type=button] {
padding: 0px;
width: 170px;
margin-left: 15px;
color: #7ED321;
}
.NewJIRAForm input[type=button]:hover {
color: #FFFFFF;
background-color: #7ED321;
}
input[type=button]:focus, button:focus, input[type=text]:focus {
outline: none;
}
#newJIRAProjectKey {
padding-left: 10px;
width: 160px;
margin-left: 15px;
}
input[type=button], button {
height: 38px;
border-radius: 6px;
border-style: none;
font-size: 12px;
font-weight: bold;
}
.newJIRABoardNameInputField, .newJIRABaseURLField, .newJIRABoardKeyInputField {
height: 36px;
border-radius: 6px;
border-style: none;
padding-left: 10px;
}
.goToInput {
height: 36px;
border-radius: 6px;
padding-left: 10px;
width: 130px;
border-style: solid;
margin-right: 15px;
}
.addFirstRowForm, .addSecondRowForm {
margin-top: 15px;
}
.goNewButton, .goCurrentButton {
margin-right: 15px;
background-color: #7ED321;
color: white;
width: 90px;
}
.goNewButton:hover, .goCurrentButton:hover {
background-color: #417505;
}
.removeButton {
background-color: #D0021B;
color: white;
width: 65px;
}
.removeButton:hover {
background-color: #961424;
}
.removeButton:focus, .goNewButton:focus, .goCurrentButton:focus {
outline: 0;
}
.quickJIRARow {
margin-left: 15px;
margin-top: 7.5px;
z-index: 2;
display: inline-block;
}
.CurrentQuickJIRAs .quickJIRARow:last-child {
margin-bottom: 7.5px;
}
.quickJIRARowButtons {
display: inline-block;
}
.CurrentQuickJIRAs {
max-height:255px;
overflow-y:scroll;
}
.quickJIRARow a {
text-decoration: none;
color: #000;
text-align: right;
display: inline-block;
white-space: nowrap;
width: 130px;
margin-right: 10px;
font-weight: bold;
font-size: 12px;
z-index: 2;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
position: relative;
}
.quickJIRARow a:hover {
color: blue;
}
Ссылка на репозиторий (текущая проблема, воспроизводимая в master): https://github.com/bzellman/quickJira
Комментарии:
1. Звучит как ошибка. См . crbug.com и сообщите об этом, если это не зафиксировано в Canary и нет никаких предыдущих отчетов.
2. Спасибо! Perforce немного лучше в Canary. Я подниму эту проблему, если не смогу найти соответствующий запрос и постараюсь минимизировать влияние на мою сторону
3. то же самое происходит и со мной — вы отправили отчет об ошибке?
4. Привет, извините за поздний ответ здесь. Похоже, что с аналогичными строками уже был отправлен отчет об ошибке bugs.chromium.org/p/chromium/issues/detail?id=971701